爬虫小案例:多协程工作
为提高工作效率,让多个爬虫一起工作
需要使用的库是:gevent
方式一
1.使用队列的形式:
from gevent import monkey # 从gevent库里导入monkey模块。 monkey.patch_all() # monkey.patch_all()能把程序变成协作式运行,就是可以帮助程序实现异步。 import gevent,time,requests # 导入gevent、time、requests from gevent.queue import Queue # 从gevent库里导入queue模块 start = time.time() # 记录开始时间 url_list = [ 'https://www.baidu.com/', 'https://www.sina.com.cn/', 'http://www.sohu.com/', 'https://www.qq.com/', 'https://www.163.com/', 'http://www.iqiyi.com/', 'https://www.tmall.com/', 'http://www.ifeng.com/' ] # 要爬取的网站 work = Queue() # 创建队列对象,并赋值给work。 for url in url_list: work.put_nowait(url) # 用put_nowait()函数可以把网址都放进队列里 def crawler(): while not work.empty(): # 当队列不是空的时候,就执行下面的程序。 url = work.get_nowait() # 用get_nowait()函数可以把队列里的网址都取出。 r = requests.get(url) # 抓取网站内容 print(url, work.qsize(), r.status_code) # 打印网址、队列长度、抓取请求的状态码 task_list = [] # 创建空的任务列表 for i in range(2): # 相当于创建了2个爬虫 task = gevent.spawn(crawler) # 用gevent.spawn()函数创建执行crawler()函数的任务。 task_list.append(task) # 往任务列表添加任务 gevent.joinall(task_list) # 用gevent.joinall方法,执行任务列表里的所有任务,就是让爬虫开始爬取网站。 end = time.time() # 结束时间 print(end-start) # 总用时
以上是创建了一个队列存储 url,然后让 2 个爬虫一起工作,哪个爬虫执行完了,就可以从队列中获取下一个 url 去执行
方式二
2.不使用队列,有多少个任务,就创建多少个爬虫去执行:
from gevent import monkey # 从gevent库里导入monkey模块。 monkey.patch_all() # monkey.patch_all()能把程序变成协作式运行,就是可以帮助程序实现异步。 import gevent,time,requests #导入gevent、time、requests。 start = time.time() # 记录程序开始时间。 url_list = [ 'https://www.baidu.com/', 'https://www.sina.com.cn/', 'http://www.sohu.com/', 'https://www.qq.com/', 'https://www.163.com/', 'http://www.iqiyi.com/', 'https://www.tmall.com/', 'http://www.ifeng.com/' ] #把8个网站封装成列表。 def crawler(url): #定义一个crawler()函数。 r = requests.get(url) #用requests.get()函数爬取网站。 print(url,time.time()-start,r.status_code) #打印网址、请求运行时间、状态码。 tasks_list = [] #创建空的任务列表。 for url in url_list: # 遍历url_list。 task = gevent.spawn(crawler, url) # 用gevent.spawn()函数创建任务。 tasks_list.append(task) # 往任务列表添加任务。 gevent.joinall(tasks_list) # 执行任务列表里的所有任务,就是让爬虫开始爬取网站。 end = time.time() # 记录程序结束时间。 print(end-start) #打印程序最终所需时间。
以上只有 8 个网站,数量不多,可以这样操作,但是,如果是 1000 个网站,就是一下子发起1000 次请求,这样的恶意请求,会拖垮网站的服务器。所以,这样做是不可取的,要使用队列的形式来操作。
小练习:多协程爬取豆瓣 Top250电影
from gevent import monkey # 程序异步执行 monkey.patch_all() import gevent,requests,csv from bs4 import BeautifulSoup from gevent.queue import Queue # 电影列表 movies = [] # 队列对象 work = Queue() for x in range(10): url = 'https://movie.douban.com/top250?start={}&filter='.format(x * 25) work.put_nowait(url) def moviesort(movie): return int(movie[0]) def crawler(): while not work.empty(): url = work.get_nowait() # 为躲避反爬机制,伪装成浏览器的请求头 headers = { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36 OPR/65.0.3467.78 (Edition Baidu)'} print('正请求:{}'.format(url)) res = requests.get(url, headers=headers) if res.status_code == 200: htmltext = res.text soup = BeautifulSoup(htmltext, 'html.parser') ol = soup.find('ol', class_='grid_view') for li in ol.find_all('li'): # 排名 num = li.find('div', class_='pic').find('em').text info = li.find('div', class_='info') # 标题 title = info.find('div', class_='hd').find('span', class_='title').text.strip() # 链接 link = info.find('div', class_='hd').find('a')['href'] # 评分 rating_num = info.find('span', class_='rating_num').text # 推荐语 inq = info.find('span', class_='inq').text # 上映时间、地区、类型 bd = info.find('div', class_='bd').find('p').contents[2] bd = bd.split('/') movies.append([num,title,link,rating_num,inq,bd[0].strip(),bd[1].strip(),bd[2].strip()]) else: print('{}:请求失败!'.format(url)) task_list = [] # 创建 2 个爬虫 for i in range(2): # 创建任务 task = gevent.spawn(crawler) # 把任务添加到任务列表 task_list.append(task) # 执行所有任务 gevent.joinall(task_list) # 排序 movies.sort(key=moviesort) # 打印电影数据 print(movies) # 保存数据 with open('./豆瓣Top250电影.csv', 'w', newline='', encoding='utf-8-sig') as file: # 编码utf-8-sig:支持python3,不支持python2 writer = csv.writer(file) writer.writerow(['排名', '电影名称', '链接', '评分', '推荐语', '上映时间', '地区', '类型']) for movie in movies: writer.writerow(movie) print('完毕!')
小练习:多协程爬取薄荷网食物热量
from gevent import monkey # 程序异步执行 monkey.patch_all() import gevent,requests,csv from bs4 import BeautifulSoup from gevent.queue import Queue foods = [] # 队列对象 work = Queue() url = "http://www.boohee.com/food/group/{type}?page={page}" for x in range(1, 4): for y in range(1, 4): url1 = url.format(type=x, page=y) work.put_nowait(url1) url = "http://www.boohee.com/food/view_menu?page={page}" for x in range(1, 4): url2 = url.format(page=x) work.put_nowait(url2) # 爬虫 def crwaler(): while not work.empty(): url = work.get_nowait() headers = { 'user-agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36 OPR/66.0.3515.36 (Edition Baidu)' } print("正请求网页:{}".format(url)) res = requests.get(url, headers=headers) res.encoding = 'utf-8' soup = BeautifulSoup(res.text, 'html.parser') foodlist = soup.find('ul', class_='food-list').find_all('li', class_='item') for food in foodlist: box = food.find("div", class_='text-box') # 名称 name = box.find("a")['title'] # 链接 link = "http://www.boohee.com" + box.find("a")['href'] # 热量 heat = box.find("p").text.strip() foods.append([name, link, heat]) # 任务列表 task_list = [] # 创建 2 个爬虫 for i in range(2): # 创建任务 task = gevent.spawn(crwaler) task_list.append(task) # 执行所有任务 gevent.joinall(task_list) print(foods) # 保存数据 with open('./食物热量.csv', 'w', newline='', encoding='utf-8-sig') as file: writer = csv.writer(file) writer.writerow(['名称', '链接', '热量']) for food in foods: writer.writerow(food) print("完毕!")