python异步爬虫(1):引入+线程池实战
? 使用异步爬虫可以实现高性能高效率的数据爬取操作
一.引入
先运行下面这个程序:
import requests
headers = {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.164 Safari/537.36"
}
urls = ['https://www.sogou.com/',
'https://cn.bing.com/?mkt=zh-CN&mkt=zh-CN',
'https://www.baidu.com/']
def get_content(url):
print("正在爬取",url)
resp=requests.get(url=url,headers=headers)
if resp.status_code == 200:
return resp.content
def parse_content(content):
print("响应数据的长度为",len(content))
for url in urls:
content =get_content(url)
parse_content(content)
输出结果的时候,明显可以观察到爬取过程是一个阻塞操作,是耗时过程
也可以加入时间来观察:
for url in urls:
time_start=time.time()
content=get_content(url)
parse_content(content)
time_end=time.time()
time_c=time_end-time_start
print("time_cost",time_c,'s')
这是一个单线程环境,如果要爬取100个,10000个,那么…显然效率很低
那么效率高的异步爬虫有哪些 方式呢:
- 多线程、多进程
- 好处:可以为相关阻塞的操作单独开启线程或进程,阻塞操作可以异步执行
- 弊端:无法无限制开启多线程或者多进程(CPU有限制)
- 线程池、进程池
- 好处:可以降低系统对进程或线程创建和销毁的频率,从而降低系统开销
- 弊端:池中线程或进程的数量有上限
二.线程池实战
? 线程池的解释
? ★这个视频里的动画解释很生动:B站 楠哥讲解视频
? 昨天pymongo实战那篇文章里面搜到的Xpath返回为空的那篇文章里面也提到了这个,并且更加推荐resp.content
? 还可以看看这个:python中的requests,response.text与response.content ,及其编码
1.简单模拟一下
单线程:
import time
def get_page(str):
print("正在爬取:",str)
time.sleep(2)
print('下载成功:',str)
name_list=['a','b','c','d']
start_time=time.time()
for i in range(len(name_list)):
get_page(name_list[i])
end_time=time.time()
print('%d second'%(end_time-start_time))
换成线程池来处理:
import time
from multiprocessing.dummy import Pool
start_time = time.time()
def get_page(str):
print("正在爬取:", str)
time.sleep(2)
print('下载成功:', str)
name_list = ['a', 'b', 'c', 'd']
pool = Pool(4)
pool.map(get_page, name_list)
end_time = time.time()
print('%d second' % (end_time-start_time))
改成%f输出的是2.029824 second
? 关于python的map()函数
2.实战:线程池爬取蔬菜网市场价格前200页
在pymongo实战(2)那篇博客的基础上稍作修改,加上线程池
代码
import pymongo
import requests
from lxml import etree
from concurrent.futures import ThreadPoolExecutor
myClient = pymongo.MongoClient("localhost")
myDb = myClient["vegetable2"]
myCol =myDb["price"]
headers={"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.164 Safari/537.36"}
def download_onepage(url,num_1):
resp = requests.get(url,headers=headers)
resp.encoding=resp.apparent_encoding
pageText = etree.HTML(resp.text)
rows = pageText.xpath('/html//div[@class="pri_k"]/p')
num_2=0
for row in rows:
date = row.xpath('./span[1]/text()')[0].replace('[', '').replace(']', '')
variety = row.xpath('./span[2]/text()')[0]
market = row.xpath('./span[3]/a/text()')[0]
min_price = row.xpath('./span[4]/text()')[0]
max_price = row.xpath('./span[5]/text()')[0]
mean_price = row.xpath('./span[6]/text()')[0]
measure_unit = row.xpath('./span[7]/text()')[0]
kv_dict={"date":date,"variety":variety,"market":market,
"min_price":min_price,"max_price":max_price,
"mean_price":mean_price,"measure_unit":measure_unit}
myCol.update_one({"index":f"{num_1}{num_2}"},{"$set":kv_dict},upsert=True)
num_2+=1
print(f"{url}抓取完毕")
resp.close()
if __name__=="__main__":
with ThreadPoolExecutor(50) as t:
for i in range(1,201):
t.submit(download_onepage,f'http://www.vegnet.com.cn/Price/List_p{i}.html',i)
print("done!")
? ThreadPoolExecutor(线程池介绍)
? python线程池 ThreadPoolExecutor 使用详解
遇到的问题
if __name__ == '__main__':
一开始一直不运行,结果发现'__main__' 前后两个下划线漏了…
数据条数
每一页是26行数据,但是运行了好几次都是4715条数据,按理说前200页应该是5200条,目前还不知道原因…
好像被封IP了…
不过好像,过一会儿又解封了…
又能继续爬取
线程池爬取不能保证数据的有序性
一方面查看的时候可以用sort解决,但是线程池会把页数打乱…
目前还没有进一步想法
|