目录
1 动态抓取技术简介
- 异步更新技术——AJAX
AJAX(Asynchronous Javascript And XML,异步JavaScript和XML)的价值在于通过在后台与服务器进行少量的数据交换就可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下对网页的某部分进行更新。一方面减少了网页重复内容的下载,另一方面节省了流量,因此AJAX得到了广泛的使用。
爬取使用AJAX加载的动态网页里的动态加载的内容有两种动态网页抓取的方法:
- 通过浏览器审查元素解析真实网页地址
- 使用Selenium模拟浏览器的方法
2 解析真实地址抓取
以《Python网络爬虫从入门到实践(第2版)》作者博客的Hello World文章为例,目标是抓取文章下的所有评论。文章网址为:
步骤01 打开“检查”功能。用Chrome浏览器打开Hello World文章。右击页面的任意位置,在弹出的快弹菜单中单击“检查”命令。
步骤02 找到真实的数据地址。单击页面中的Network选项,然后刷新网页。此时,Network会显示浏览器从网页服务器中得到的所有文件,一般这个过程成为“抓包”。
从文件中快速找到评论数据所在文件的方法:search评论内容可以快速定位具体的评论所在位置的文件。
步骤03 爬取真实评论数据地址。既然找到了真实的地址,接下来就可以直接用requests请求这个地址获取数据了。
步骤04 从json数据中提取评论。可以使用json库解析数据,从中提取想要的数据。
import requests
import json
link = "https://api-zero.livere.com/v1/comments/list?callback=jQuery112408626354130815113_1646567623361&limit=10&repSeq=4272904&requestPath=/v1/comments/list&consumerSeq=1020&livereSeq=28583&smartloginSeq=5154&code=&_=1646567623363"
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36'}
r = requests.get(link, headers=headers)
# 获取json的string
json_string = r.text
json_string = json_string[json_string.find('{'):-2] # 从第一个左大括号提取,最后的两个字符-括号和分号不取
json_data = json.loads(json_string)
comment_list = json_data['results']['parents']
for eachone in comment_list:
message = eachone['content']
print(message)
接下来可以用for循环爬取多页评论数据,可以通过对比不同页面真实地址,发现其参数的不同之处,通过改变折现参数值实现换页。
import requests
import json
def single_page_comment(link):
headers = {
'User-Agent': 'https://api-zero.livere.com/v1/comments/list?callback=jQuery112407919796508302595_1646571637355&limit=10&offset=2&repSeq=4272904&requestPath=/v1/comments/list&consumerSeq=1020&livereSeq=28583&smartloginSeq=5154&code=&_=1646571637361'}
r = requests.get(link, headers=headers)
# 获取json的string
json_string = r.text
json_string = json_string[json_string.find('{'):-2] # 从第一个左大括号提取,最后的两个字符-括号和分号不取
json_data = json.loads(json_string)
comment_list = json_data['results']['parents']
for eachone in comment_list:
message = eachone['content']
print(message)
max_comments = 50 # 设置要爬取的最大评论数
for page in range(1,max_comments // 10 + 1):
link1 = "https://api-zero.livere.com/v1/comments/list?callback=jQuery112407919796508302595_1646571637355&limit=10&offset="
link2 = "&repSeq=4272904&requestPath=/v1/comments/list&consumerSeq=1020&livereSeq=28583&smartloginSeq=5154&code=&_=1646571637361"
page_str = str(page)
link = link1 + page_str + link2
print(link)
single_page_comment(link)
3 通过Selenium模拟浏览器抓取
有些网站非常复杂,使用“检查”功能很难找到调用的网页地址。除此之外,有些数据的真实地址的URL也十分复杂,有些网站为了规避这些抓取会对地址进行加密,造成其中的一些变量让人摸不着头脑,因此,这里介绍另一种方法,即使用浏览器渲染引擎。直接使用浏览器在显示网页时解析HTML、应用CSS样式并执行JavaScript的语句。通俗来讲就是使用浏览器渲染方法将爬取动态页面变成爬取静态页面。
在这里,使用Python的Selenium库模拟浏览器完成抓取。Selenium是一个用于Web应用程序测试的工具。
3.1 Selenium的基本介绍
浏览器驱动下载地址:
Chrome: https://sites.google.com/chromium.org/driver/
Edge: Microsoft Edge Driver - Microsoft Edge Developer
使用Selenium打开浏览器和一个网页,代码如下:
from selenium import webdriver
driver = webdriver.Firefox()
driver.get("http://www.santostang.com/2018/07/04/hello-world/")
3.2 Selenium实践案例
步骤01 找到评论的HTML代码标签。使用Chrome打开文章页面,右击页面,在弹出的快捷菜单中单击“检查”命令。
步骤02 尝试获取一条评论数据。在原来打开页面的代码数据上使用以下代码,获取第一条评论数据。
from selenium import webdriver
driver = webdriver.Firefox()
driver.get("http://www.santostang.com/2018/07/04/hello-world/")
# 转化iframe
driver.switch_to.frame(driver.find_element(by='css selector', value='iframe[title="livere-comment"]'))
# 获取css标签为div.reply-content
comment = driver.find_element(by='css selector', value='div.reply-content')
content = comment.find_element(by='tag name', value='p')
print(content.text)
3.3 Selenium获取文章的所有评论
如果要获取所有评论,需要脚本程序能够自动点击“+10查看更多”,这样才能将所有评论显示出来。因此,我们需要找到“+10查看更多”的元素地址,然后让Selenium模拟单击并加载评论。具体代码如下:
from selenium import webdriver
import time
driver = webdriver.Firefox()
driver.implicitly_wait(10) # 隐性等待,最长等10秒
driver.get("http://www.santostang.com/2018/07/04/hello-world/")
time.sleep(5)
# 下滑到页面底部(左下角)
driver.execute_script("window.scrollTo(0,document.body.scrollHeight);")
print("wait for 3 seconds")
time.sleep(3)
for i in range(1, 19):
# 转换iframe,再找到查看更多,点击
driver.switch_to.frame(driver.find_element(by="css selector", value="iframe[title='livere-comment']"))
# load_more = driver.find_element(by="css selector", value="button[data-page=\'%d\']'%i")
if i > 11:
x_path = './/div[@class="more-wrapper"]/button[' + str(i - 9) + ']'
else:
x_path = './/div[@class="more-wrapper"]/button[' + str(i) + ']'
load_more = driver.find_element(by="xpath", value=x_path)
load_more.send_keys("Enter") # 点击前先按下Enter,可以解决因跳转点击时出现失去焦点而导致的click单击无效的情况
load_more.click()
# 把iframe又转回去
driver.switch_to.default_content()
print("Click and waiting loading --- please waiting for 5s")
time.sleep(5)
driver.switch_to.frame(driver.find_element(by="css selector", value="iframe[title='livere-comment']"))
comments = driver.find_elements(by="css selector", value="div.reply-content")
for each_comment in comments:
content = each_comment.find_element(by="tag name", value="p")
print(content.text)
driver.switch_to.default_content()
driver.execute_script("window.scrollTo(0,document.body.scrollHeight);")
time.sleep(2)
Selenium的常见操作元素方法如下:
- Clear:清除元素的内容
- send_keys:模拟按键输入
- Click:单击元素
- Submit:提交表单
from selenium import webdriver
import time
driver = webdriver.Firefox()
driver.implicitly_wait(10) # 隐性等待,最长等10秒
driver.get("http://www.santostang.com/")
time.sleep(5)
user = driver.find_element(by="name", value="username")
user.send_keys("123456")
pwd = driver.find_element(by="password") # 找到密码输入框
pwd.clear() # 清除密码输入框内容
pwd.send_keys("******") # 在框中输入密码
driver.find_element(by="id", value="loginBtn").click() # 单击登录
Selenium除了可以实现简单的鼠标操作,还可以实现复杂的双击、拖拽等操作。此外,Selenium还可以获得网页中各个元素的大小,甚至可以进行模拟键盘的操作。
3.4 Selenium的高级操作
常用的加快Selenium的爬取速度的方法有:
- 控制CSS的加载
- 控制图片文件的显示
- 控制JavaScript的运行
3.4.1 控制CSS
在抓取过程中仅仅抓取页面的内容,CSS样式文件是用来控制页面的外观和元素放置位置的,对内容并没有影响,所以我们可以通过限制网页加载CSS,从而较少抓取时间,其代码如下:
# 控制CSS
from selenium import webdriver
# 使用fp控制CSS的加载
fp = webdriver.FirefoxOptions()
fp.set_preference("permissions.default.stylesheet", 2)
driver = webdriver.Firefox(options=fp)
driver.get("http://www.santostang.com/2018/07/04/hello-world/")
3.4.2 限制图片的加载
如果不需要抓取网页上的图片,最好可以禁止图片加载,限制图片的加载可以帮助我们极大地提高网络爬虫的效率。
# 限制图片的加载
from selenium import webdriver
fp = webdriver.FirefoxOptions()
fp.set_preference("permissions.default.image", 2)
driver = webdriver.Firefox(options=fp)
driver.get("http://www.santostang.com/2018/07/04/hello-world/")
3.4.3 控制JavaScript的运行
如果需要抓取的内容不是通过JavaScript动态加载得到的,我们可以通过禁止JavaScript的执行来提高抓取的效率。因为大多数网页都会利用JavaScript异步加载很多的内容,这些内容不仅是我们不需要的,它们的加载还浪费了时间。
# 限制JavaScript的执行
from selenium import webdriver
fp=webdriver.FirefoxOptions()
fp.set_preference("javascript.enabled",False)
driver=webdriver.Firefox(options=fp)
driver.get("http://www.santostang.com/2018/07/04/hello-world/")
参考资料
[1] 唐松.2019.Python网络爬虫从入门到实践(第2版)[M].北京:机械工业出版社