为了回答这个问题,我们需要使用Python的一个库:asyncio。asyncio是Python的标准库,用于编写异步程序。异步程序在一些关键点上会挂起执行,让其他的协程有机会运行。
首先,我们需要一个库来帮助我们下载网页,这个库叫requests。
下面是一个简单的示例,使用asyncio和requests库来异步下载网页:
import asyncio
import requests
async def fetch_html(url):
async with requests.get(url) as response:
return response.text
urls = ['http://example.com/{}'.format(i) for i in range(20)]
loop = asyncio.get_event_loop()
htmls = loop.run_until_complete(asyncio.gather(*(fetch_html(url) for url in urls)))
for html in htmls:
print(html)
然而,这个例子只能在网页的数量不太多的时候使用,因为如果网页数量太多,会有一个问题就是会有很多的网络请求,可能会让服务器认为你是一个爬虫而封你的IP。
为了解决这个问题,我们可以使用asyncio.Semaphore来限制同时进行的网络请求的数量。
import asyncio
import requests
async def fetch_html(url):
async with requests.get(url) as response:
return response.text
async def limited_fetch_html(sem, url):
async with sem:
return await fetch_html(url)
sem = asyncio.Semaphore(5) # 最多同时进行5个网络请求
urls = ['http://example.com/{}'.format(i) for i in range(20)]
loop = asyncio.get_event_loop()
htmls = loop.run_until_complete(asyncio.gather(*(limited_fetch_html(sem, url) for url in urls)))
for html in htmls:
print(html)
这样,我们就可以在不被服务器封禁IP的情况下,高效率的下载多个网页。
然而,上述代码只能应对简单的情况,如果网页中含有js动态加载的内容,上述方法就不能获取到完整的内容。这个时候,我们可以使用webdriver来驱动浏览器,然后用phantomjs作为无头浏览器。
from selenium import webdriver
driver = webdriver.PhantomJS()
driver.get('http://example.com')
html = driver.page_source
print(html)
driver.quit()
然而,webdriver是同步的,如果我们想要异步下载多个网页,我们可以把webdriver封装在一个异步函数中。
import asyncio
from selenium import webdriver
async def fetch_html(url):
driver = webdriver.PhantomJS()
driver.get(url)
html = driver.page_source
driver.quit()
return html
urls = ['http://example.com/{}'.format(i) for i in range(20)]
loop = asyncio.get_event_loop()
htmls = loop.run_until_complete(asyncio.gather(*(fetch_html(url) for url in urls)))
for html in htmls:
print(html)
然而,上述代码在多个协程同时运行时,可能会出现webdriver启动不起来的问题,因为phantomjs不支持多个进程同时启动。
为了解决这个问题,我们可以使用一个单例模式的webdriver。
import asyncio
from selenium import webdriver
class SinglePhantom: