Python爬虫-速度(3)
Python爬虫-速度(3)
018.11.11
前言
早之前是以为会一口气把爬虫这个系列了结的,但不知何故,居然没做到——我肯定不会怪罪自己的拖延症呀!只是有开头就得有结尾,毕竟我是那么那么注重仪式感的人。
再从GitHub把代码clone下来,发现不能用了。刨根问底,居然是学校就业网站改版,这倒令我惊奇。会是因为哪个老师或者学生确实无聊所以动了“美好校园”的念头?缘由于我来说肯定是不得而知的。
重写一份吧。结构上相比两个月前的代码是有所优化的,但不至于天壤之别。相似处多,所以不再单独对此次基础代码进行说明。
此篇目的是:基于(2)的提速方法,改写(1)爬虫代码。当然了,因为网页改版,接下来我会是基于新的代码来改写。
那就开始啦。
普通爬虫
此次代码对应目录:NewSchoolJobsCrawl
效果如下:
多次运行,整个程序跑完在60s左右。
多进程提速
在ProcessPoolExecutor助攻下,多进程爬虫的实现真的会很简单。我们只需要把浏览页处理的逻辑提取出来(我把它放到了main()中),然后构建一个进程池去执行这部分的代码就好。这样说会很抽象,将下面的代码与普通爬虫的代码做对比,理解起来容易些。
def main(url):
html, __ = get_one_html(url)
if html:
for title, url in extract_title_url(html):
# 只保留公司名
companyName = re.split(r"(|\(|\s", title)[0]
mkdir(companyName)
# 详情页面,详情页链接
detailHtml, curUrl = get_one_html(join_url(url))
# 详情页招聘信息,doc链接
positionInfo, docLink = extract_detail_data(detailHtml)
save_to_text(companyName, positionInfo)
download_doc(companyName, docLink, curUrl)
print(companyName, curUrl)
if __name__ == "__main__":
start = time.time()
init()
# fstring python3.7特性
suffix = (f"/{i}" for i in range(1, 23))
urls = [re.sub(r".htm", str(page)+".htm", MAIN_URL) for page in chain([""], suffix)]
with ProcessPoolExecutor() as pool:
pool.map(main, urls)
end = time.time()
takeTime = end - start
print(f"【总共耗时】{takeTime}")
平均耗时:10s上下
多线程提速
同样的,有了ThreadPoolExecutor后,多线程也变得简单起来。除了类名,其他部分可以完全和多进程的代码一样。
from concurrent.futures import ThreadPoolExecutor
...
if __name__ == "__main__":
...
with ThreadPoolExecutor() as pool:
pool.map(main, urls)
...
平均耗时:9s+
异步协程提速
异步协程的改动会大一些,这是因为requests模块不支持异步操作。我们需要借助aiohttp来封装一个get请求方式的get()方法如下:
async def get(url, referer=None):
headers = HEADERS
if referer is not None:
r = {"Referer": str(referer)}
headers.update(r)
async with aiohttp.ClientSession() as session:
async with session.get(url, headers=headers) as response:
text = await response.read()
url = response.url
assert response.status == 200
return text, url
通过这个方法,就可以实现异步操作了。
其他地方也需要做相应的调整,比如async修饰的函数,调用时需要await关键字,但都是小问题。更多需要在意协程是如何使用的。
if __name__ == "__main__":
start = time.time()
init()
# fstring python3.7特性
suffix = (f"/{i}" for i in range(1, 23))
# 注意这一部分的改动
urls = [re.sub(r".htm", str(page)+".htm", MAIN_URL) for page in chain([""], suffix)]
tasks = [asyncio.ensure_future(main(url)) for url in urls]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
loop.close()
# --------注意线-------------
end = time.time()
takeTime = end - start
print(f"【总共耗时】{takeTime}")
平均耗时:9s+
最后
感觉这一次写得很“潦草”,这是没办法的事儿。因为以上三个内容(多进程,多线程,协程)每一个都可以拿出来单独写好长一篇。可我这个系列是:爬虫。为了不客夺主位,只好一切从简。
但无论如何是有说到的爬虫提速的,让我的爬虫系列有头有尾——虽然虎头蛇尾。
这次展示的三个提速代码,并非将速度达到极致,许多地方还可以优化,但我并不建议如此。因为出于兴趣爱好的爬虫,真的没有必要分秒必争,况且网站的运维人员也需要休息、娱乐,也需要陪陪家人。而不是一直待公司跟你玩儿“猫捉老鼠”吧!
完整代码已经上传GitHub