2020农历新年前后,一场新型肺炎病毒席卷全国,截止今天感染人数74000余人,响应号召在家呆了月余没有出门。决定重新学习多线程,更加深入的理解多线程,今天尝试一下就遇到一个问题,困扰两天,一经解决豁然开朗,算是一个小心得吧,那就是线程包裹的是方法,一个方法解决一个问题,把所有的相关代码都写到方法里边,这样爬虫才能起到作用,如果有代码留在外边,则不起作用。表达能力有限,看代码:
爬取不羞涩图片网站的图片
import time
import requests
from bs4 import BeautifulSoup
import threading
class Buxiuse(object):
def __init__(self):
self.i = 1
url = 'https://www.buxiuse.com/?cid=3&page={}'
self.page_urls = [url.format(i) for i in range(1, 11)]
def parse_html(self, url):
header = {
'User - Agent': 'Mozilla / 5.0(Windows NT 10.0;Win64;x64) AppleWebKit / 537.36(KHTML, likeGecko) '
'Chrome / 80.0.3987.106Safari / 537.36'
}
res = requests.get(url, headers=header)
return res.content.decode('utf-8')
# 解析页面 获得图片url列表
def get_url_list(self, content):
bs = BeautifulSoup(content, 'html.parser')
img_list = bs.find_all('img', class_='height_min')
return [i.get('src') for i in img_list]
# 保存图片
def save_pic(self, cont):
with open('1/{}.jpg'.format(self.i), 'wb') as f:
f.write(cont)
print('{}.jpg 下载完成!'.format(self.i))
self.i += 1
def run(self):
start = time.time()
t_list = []
# 遍历取出每一页的url
for url in self.page_urls:
# 获得页面里的 图片url列表
url_list = self.get_url_list(self.parse_html(url))
# 遍历 图片url列表
for l in url_list:
cont = requests.get(l).content
t = threading.Thread(target=self.save_pic, args=(cont,))
t_list.append(t)
t.start()
for i in t_list:
i.join()
end = time.time()
print('总共时间:{}'.format(start - end))
if __name__ == '__main__':
demo = Buxiuse()
demo.run()
结构比较简单,爬取前10页的图片,也没有做信号量限制线程数。运行结果是38秒,比没有使用多线程还多出一秒。。。。
多次尝试,查资料没有所获,直到看到了这一行:
run()方法里的 cont = requests.get(l).content
作用是解析图片url获得图片的二进制代码,保存图片。
由于刚写这个代码的时候,没有用面向对象,改代码的时候,就把这行忽略了,仔细观察save_pic()方法,参数就是这个cont。
实际上,这句代码应该写在save_pic()方法里面,他出了给这个方法提供参数以外没有其他作用,所以我改了一下:
import time
import requests
from bs4 import BeautifulSoup
import threading
class Buxiuse(object):
def __init__(self):
self.i = 1
url = 'https://www.buxiuse.com/?cid=3&page={}'
self.page_urls = [url.format(i) for i in range(1, 11)]
def parse_html(self, url):
header = {
'User - Agent': 'Mozilla / 5.0(Windows NT 10.0;Win64;x64) AppleWebKit / 537.36(KHTML, likeGecko) '
'Chrome / 80.0.3987.106Safari / 537.36'
}
res = requests.get(url, headers=header)
return res.content.decode('utf-8')
# 解析页面 获得图片url列表
def get_url_list(self, content):
bs = BeautifulSoup(content, 'html.parser')
img_list = bs.find_all('img', class_='height_min')
return [i.get('src') for i in img_list]
# 保存图片
def save_pic(self, url):
cont = requests.get(url).content
with open('1/{}.jpg'.format(self.i), 'wb') as f:
f.write(cont)
print('{}.jpg 下载完成!'.format(self.i))
self.i += 1
def run(self):
start = time.time()
t_list = []
# 遍历取出每一页的url
for url in self.page_urls:
# 获得页面里的 图片url列表
url_list = self.get_url_list(self.parse_html(url))
# 遍历 图片url列表
for l in url_list:
t = threading.Thread(target=self.save_pic, args=(l,))
t_list.append(t)
t.start()
for i in t_list:
i.join()
end = time.time()
print('总共时间:{}'.format(start - end))
if __name__ == '__main__':
demo = Buxiuse()
demo.run()
运行结果是 7秒多点。。。。多线程起作用了。
为啥呢
关键就是上边那一句,造成了cont只有一个,线程再多也没有用。
这段代码问题不少,如果内容很多会开过多线程,资源占用过多,或容易被反爬,而且运行以后发现,快是快了,可是爬下来的东西却比网页上少,这就是线程间资源抢夺的问题了。