首先:
分析b站的番剧索引网页:https://www.bilibili.com/anime/index/
由图可知我们我们想要的图片是动态加载的,request请求不到。这时我们可以用selenium模块解决,但是该模块爬取速度太慢了,我们这里采用另一种方法。
另一种方法则是找出加载出这些数据的文件,有时这些动态的数据会被直接放在js中,有时会向服务器发送请求来得到数据,有一种常用的请求方式就是Ajax。我们尝试寻找该请求中的数据
这时我们发现在以result?开头的这个请求中发现我们要的数据,其中的cover下面的网址或许就是我们要的封面,打开一个看看果然是我们要的封面大图。
好,那么查看这个ajax请求的表单数据
我这里是在b站番剧索引的第一页,这时表单数据中的page为1,我翻到第二页,找到对应的表单数据。对比发现只有page变成了2,其他数据相同。
好,那么最后找到该请求对应的网址,把表单数据拼接上去,即可得到我们要的网址。
下面开始敲代码,首先拼接出网址
import requests
from concurrent.futures import ThreadPoolExecutor # 线程池模块
import urllib.parse # 这里要拼接出我们要的网址,需要urldecode这个函数,这个函数可以让一个字典类型的数据转化为网址
import re # 正则表达式模块
import time # 计算时间
url = "https://api.bilibili.com/pgc/season/index/result" # 网址的前一部分,都是一样的
head = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36 Edg/90.0.818.66'
}
urls = [] # 存放拼接好的网址
images = [] # 存放提取出来的图片网址
def get_url(page,url): # 该函数获得网址
t_dict = {
'season_version': -1,
'area': -1,
'is_finish': -1,
'copyright': -1,
'season_status': -1,
'season_month': -1,
'year': -1,
'style_id': -1,
'order': 3,
'st': 1,
'sort': 0,
'page': page,
'season_type': 1,
'pagesize': 20,
'type': 1
} # 我们分析出的表单数据,其他不变page作为函数参数让我们传入
urls.append(url+'?'+urllib.parse.urlencode(t_dict)) # 拼好一个网址放入列表
for page in range(101):
get_url(page, url)
for i in urls:
print(i) # 打印看看是否成功
成功获得网址,接下来是完整代码
import requests
from concurrent.futures import ThreadPoolExecutor # 线程池模块
import urllib.parse # 这里要拼接出我们要的网址,需要urldecode这个函数,这个函数可以让一个字典类型的数据转化为网址
import re # 正则表达式模块
import time # 计算时间
url = "https://api.bilibili.com/pgc/season/index/result" # 网址的前一部分,都是一样的
head = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36 Edg/90.0.818.66'
}
urls = [] # 存放拼接好的网址
images = [] # 存放提取出来的图片网址
def get_url(page,url): # 该函数获得网址
t_dict = {
'season_version': -1,
'area': -1,
'is_finish': -1,
'copyright': -1,
'season_status': -1,
'season_month': -1,
'year': -1,
'style_id': -1,
'order': 3,
'st': 1,
'sort': 0,
'page': page,
'season_type': 1,
'pagesize': 20,
'type': 1
} # 我们分析出的表单数据,其他不变page作为函数参数让我们传入
urls.append(url+'?'+urllib.parse.urlencode(t_dict)) # 拼好一个网址放入列表
def get_request(url): # 该函数请求拼接好的网址
html = requests.get(url, headers=head).json()
return str(html) # 返回的数据中有我们要的封面的网址
def get_image(url): # 该函数打开每张图片的网址
return requests.get(url,headers=head).content # 返回二进制数据,存入到本地
start =time.time() # 计时开始
find_image = re.compile(r"'cover': '(.*?)'") # 定义正则表达式
for page in range(1, 101): # page从1到100
get_url(page, url) # 将100个网址全存放到urls列表中
with ThreadPoolExecutor() as pool1: # 开启线程池
t_html = pool1.map(get_request, urls)
for i in t_html: # 遍历每张网页
for image in re.findall(find_image, i): # 找到每张网页中的20个封面网址
images.append(image) # 存入列表
# 这时列表images中就是我们需要的封面网址了,有2000个网址
with ThreadPoolExecutor() as pool2:
t_image = pool2.map(get_image, images) # 开启线程池,获得二进制数据
s=1
for i in t_image: # 存储数据
file = open('D:/桌面等/桌面/Python/test/image'+str(s)+'.png', 'wb')
file.write(i)
s = s+1
file.close()
print("time", time.time()-start) # 计时结束,打印时间
爬取成功,耗时两分钟,用的校园网,应该能更快。
总结:
1,这里可以看出,使用多线程能极大的提高爬取效率,如果用selenium可能要二十几分钟,翻页要时间,单线程爬取2000张图片非常慢。
2,除了多线程,还有多进程和多协程也可以加快效率,但是应用的地方不一样。
3,并不是只要用了多线程就能加快效率,python的多线程只能提高io复杂度较高的项目,如果是cpu复杂度高的的话,用多线程甚至比单线程时间还久,有线程切换的时间。所以cpu复杂度高的项目,就可以用多进程来提高效率。