一、并发爬取数据
当我们需要爬取的 url 链接非常多的时候,用 for 循环对所有链接进行访问显然是非常耗时的。
怎么提高爬虫效率呢?
我们可以使用并发来对URL进行访问以爬取数据。
有以下三种并发方式
- 多线程(threading)
- 多进程(multiprocessing)
- 协程(gevent)
先来试试多进程
二、 多线程example
import requests
import time
from multiprocessing.dummy import Pool
def get_url(url):
html = requests.get(url)
#print(html.url)
urls = ["http://www.mmjpg.com/home/{}".format(i) for i in range(1,40)]
time1 = time.time()
for url in urls:
get_url(url)
time2 = time.time()
print('单线程耗时' + str(time2 - time1))
pool = Pool(4)
time3 = time.time()
results = pool.map(get_url, urls)
pool.close()
pool.join()
time4 = time.time()
print('多线程耗时' + str(time4 - time3))
先来试试请求 40 个网页分别的耗时
>>>
=================== RESTART: E:\Python项目\爬阿爬\多线程,进程\多进程.py ===================
单线程耗时3.8298497200012207
多线程耗时3.5330474376678467
差别不是太大,但请求100呢。把 range 的范围改到 100,再运行试试
=================== RESTART: E:\Python项目\爬阿爬\多线程,进程\多进程.py ===================
单线程耗时16.267414093017578
多线程耗时4.5447304248809814
>>>
差距已经很大了
三、多线程爬取
我们以爬取 实验楼 的课程为例。
分析下url,可以发现改变 page 的参数就可以切换页数了,那么共有几页呢,我们只需爬取页面下方的这个系列的数值,倒二个就是最大的页面数。
代码如下
>>> link = 'https://www.shiyanlou.com/courses/?course_type=all&tag=all&fee=all&page=1'
>>> r = requests.get(link)
>>> bs0bj = BeautifulSoup(r.text, 'lxml')
>>> pages = bs0bj.select('body > div.container.layout-hasside.layout-margin-top > div.row > div.col-md-9.layout-body > div.content.position-relative > nav > ul > li')
>>> page = int(pages[-2].text.strip())
>>> print('共{}页'.format(page))
共24页
我们来爬取所有课程的名称 title, 说明 introduce ,热度 num。
如果要爬取别的课程只需改下 url 的 tag 参数就行
总的代码如下
import requests
from bs4 import BeautifulSoup
from multiprocessing.dummy import Pool
def get_html(page):
url = 'https://www.shiyanlou.com/courses/?course_type=all&tag=all&fee=all&page={}'.format(page)
print('第{}页'.format(page))
html = requests.get(url)
soup = BeautifulSoup(html.text, 'lxml')
titles = soup.find_all(class_='course-name')
introduces = soup.find_all(class_='course-desc')
nums = soup.find_all(class_='course-per-num pull-left')
for title, num, introduce in zip(titles, nums, introduces):
data = {
'title' : title.get_text(),
'num' : num.get_text().strip(),
'introduce' : introduce.get_text()
}
print(data)
if __name__ == '__main__':
link = 'https://www.shiyanlou.com/courses/?course_type=all&tag=all&fee=all&page=1'
r = requests.get(link)
bs0bj = BeautifulSoup(r.text, 'lxml')
pages = bs0bj.select('body > div.container.layout-hasside.layout-margin-top > div.row > div.col-md-9.layout-body > div.content.position-relative > nav > ul > li')
page = int(pages[-2].text.strip())
print('共{}页'.format(page))
pool = Pool(4)
pool.map(get_html, range(1, page+1))
#pool.map_async(get_html, range(1, page+1))
#for i in range(page+1):
#pool.apply(func=get_html, args=(i,))
pool.close()
pool.join()
运行下试试看
>>>
================== RESTART: C:\Users\Why Me\Desktop\爬实验楼.py ==================
共24页
第1页第3页第5页第7页
{'num': '536', 'introduce': '本课将介绍 json 和一些常见的 json 库,并用 C++ 编写一个 json 生成器,生成 json 数据,并学习编写测试用例。', 'title': 'C++ 编写 json 生成器'}{'num': '112', 'introduce': '本训练营主要讲后门技术实战,偏重于渗透成功后的维持访问。该课程共包含 10 个实验,每个实验都提供详细的步骤和截图,其中会有专门的三节实验,专门用来讲解木马的制作,以及对生成的后门木马的源码分析 。', 'title': 'Kali 渗透测试 - 后门技术实战(10个实验)'}
{'num': '467', 'introduce': '在这个人人自拍的年代,每个人的智能手机中至少都装了一款美颜相机或者美图软件,而这些软件实现美图功能又主要是靠滤镜来实现的。而这门课程将带领大家使用 Python 编写一个简单的滤镜程序。', 'title': 'Python 实现简单滤镜'}{'num': '1381', 'introduce': '出租车是我们生活中经常乘坐的一种交通工具,但打车难的问题也限制了我们更好地利用这种交通方式。在哪些地方出租车更容易打到?在什么时候更容易打到出租车?本课程将基于某市的出租车行驶轨迹数据,带你学习如何应用Spark SQL和机器学习相关技巧,并且通过数据可视化手段展现分析结果。', 'title': '大数据带你挖掘打车的秘籍'}
...
...