一、爬取网站流程
1、分析网站的数据来源
分析网站结构,待爬取数据在哪。
2、查看页面数据是否是直接渲染的
url请求后响应后,页面数据就已经全部加载出来,为直接渲染,看能否通过requests模块获取到
(1)在开发者工具中,找到页面的url这条数据。通过这条请求的信息来进行程序的书写。
(2)在代码中通过requests模块封装请求头,查看response.text中是否有页面数据。
3、如果不是直接渲染
滚动鼠标,页面数据会不断加载,还有如显示全文,这种情况考虑用selenium或者ajax方法(XHR)查看请求。
4、如果有数据,就考虑是否有分页,分页如何实现
5、实现分页之后就可以提取数据,保存数据了
二、selenium的三种等待方式
1.强制等待
time.sleep(3)—强制让该进程休眠3秒。
2、隐式等待
driver.implicitly_wait(20)
含义:20,单位为秒,表示最大等待时长。当加载一个页面,看到页面刷新按钮不再转圈圈,即页面数据全部加载完成,才算等待结束。
浏览器数据全部加载完成包含以下内容:
(1).页面的html从服务器返回。
(2).页面的所有静态资源,js,css,img,广告,全部从服务器获取,在加载到页面中。
隐式等待的弊端:
使用等待,主要目的就是等待我们想要的数据加载出来。用隐式等待会等到所有资源都加载完成,花了一部分时间,去等待我们不想要的资源,所以隐式等待并不常用。
3、显示等待
可以设置一些条件,只要页面加载时满足这个条件,就等待完成,所以等待的终止是可以通过程序员自己来手动控制。
1、显示等待的使用
(1)第一步,导入三个模块
# 显示等待的三个重要的模块
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
(2)创建一个显示等待对象
第一个参数:表示等待作用哪那个driver。
第二个参数:最大等待时长,如果20s还没等待数据出来,就报timeout超时异常。
driver = webdriver.PhantomJS()
wait = WebDriverWait(driver,20)
(3)发送请求后,等待。
wait.until()的返回值是一个webelement对,它对应的就是选择器查找出来的那个webelement对象。
presence_of_element_located括号中必须是元组类型,指定等待结束的条件。
第一个参数:By.XPATH,表示定位的选择器的名称。还可以是By.ID、BY.CSS_SELECTOR等。
第二个参数:表示定位器locator,即要等到什么数据出现时,等待才结束。
# 请求url
driver.get(url)
# 等待
wait.until(EC.presence_of_element_located((By.XPATH,'//div[@class="recruit-list"]')))
三、使用ajax方式爬取数据
如果页面数据不是直接渲染的,而是滚动鼠标不断加载数据的方式显示数据,考虑是用ajax请求加载数据。在分析工具中选择XHR,看ajax请求情况,如果不断发送ajax,Response中有数据。则确定使用ajax方法爬取数据。
1、如何分析ajax接口
一个ajax接口,能否请求成功,最重要的就是请求参数。
请求参数的分析方法:
1.有可能一些参数是通过js加密的。
2.请求参数里面有些参数是通过之前的一个ajax中提前获取好的。
2、以爬取豆瓣电影的电影排行榜中的电影信息为例,介绍ajax方法。
# 导入模块
import requests,re,json
from lxml import etree
# 执行爬取,主要获得type值
def get_content(url,headers):
response = requests.get(url,headers=headers) # 发送请求,爬取数据
if response.status_code == 200:
return etree.HTML(response.text) # 返回element对象
return response.status_code
# 解析json数据
def parse_json(html):
json_data = json.loads(html) # 将json的字符串格式数据转为列表格式数据
for item in json_data:
print(item)
# 从ajax请求中获取数据
def parse_ajax(type_num):
# ajax请求中的基础url
base_url = 'https://movie.douban.com/j/chart/top_list?'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36',
}
'''
在不知道有多少页的情况下,用while True,当获取的数据为空时,跳出循环,
如何判定结束条件,访问ajax的Request URL如https://movie.douban.com/j/chart/top_list?type=11&interval_id=100%3A90&action=&start=800&limit=20
输入很大的页码(start的值),看返回的内容是否为'[]'
'''
i=0
while True:
# ajax请求中的参数
params = {
'type': type_num, # 电影类型
'interval_id': '100:90',
'action':'',
'start': str(i), # 页码
'limit': '20', # 每页显示数
}
# 发送请求,爬取数据,当返回为‘[]’,break
json_str = requests.get(base_url,headers=headers,params=params).text
if json_str == '[]':
break
i += 20
parse_json(json_str) # 执行解析json数据的函数
def main():
base_url = 'https://movie.douban.com/chart' # 有电影类型页的url
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36',
}
# 分析了ajax的请求参数后,发现params中的type是根据电影类型变化的,回到上一页获取type
# 获取type值
html = get_content(base_url,headers) # ,调用函数,获得element对象
a_hrefs = html.xpath('//div[@class="types"]/span/a/@href') # 使用xpath获取含有type的属性
for a_href in a_hrefs:
#print(a_href) #href="/typerank?type_name=剧情&type=11&interval_id=100:90&action="
# 使用正则获取type的值
type_p = re.compile(r'type=(.*?)&')
type_num = type_p.search(a_href).group(1)
# print(type_num)
parse_ajax(type_num) # 传入type值,调用函数,从ajax请求接口中获取数据
if __name__ == '__main__':
main()