前面爬取的页面都是静态页面,页面展示的内容都存储在HTML
源代码中。
但是,现在主流的网站都使用JavaScript
展现网页内容,和静态网页不一样的是,使用JavaScript
时,很多内容都不会出现在HTML
源码中。
因此,我们需要用到爬取动态网页的两种技术:
在爬取动态网页之前,我们需要先了解一种异步更新技术——AJAX
(异步JavaScript
和XML
)
它的价值在于通过在后台与服务器进行少量数据交换就可以使网页实现异步更新。
这意味着可以在不更新加载整个网页的情况下对网页的某部分进行更新。
例如,我们在看微博评论时,网页不会将所有评论全部展现,而是我们往下滑一段就出现一段评论,这就用到了异步更新技术
比如:我们在异步加载网站中,我们看到的评论数据就是使用JavaScript加载的,不会出现在网页源代码中。
爬取动态网页的两种方法:
Chrome右键点击“检查”命令,单击页面中的“Network”选项,然后刷新网页。此时Network会显示浏览器从网页服务器中得到的所有文件,这个过程被称为“抓包”
因为显示的文件太多,可以按crtl+F搜索想要的内容,会自动定位到该网页,例如我们根据网页的评论搜索定位
在headers中我们可以看到真实地址
import requests
import lxml.html
import json
url='https://api-zero.livere.com/v1/comments/list?callback=jQuery112404090822790751196_1609138618610&limit=10&repSeq=4272904&requestPath=%2Fv1%2Fcomments%2Flist&consumerSeq=1020&livereSeq=28583&smartloginSeq=5154&code=&_=1609138618612'
headers={
'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Mobile Safari/537.36'}
r=requests.get(url,headers=headers)
print(r.text)
open('html.txt','w',encoding='utf-8').write(r.text)
运行代码我们发现我们想要的评论数据就包含在响应文件中
接下来就是从json数据中提取评论。上述结果比较杂乱,其实这些是json数据,我们可以使用json克时解析数据,从中提取想要的信息。
这时,我们发现这个网页数据不是正确的json格式
因为我们这里不涉及数据清洗内容,所以我们把错误的数据手动删除掉(第一行{
前面的数据和结尾的);
),删除完之后即为正确的json格式,即一个字典
import json
html=open('html.txt','r',encoding='utf-8').read()
json_data=json.loads(html)
comment_list=json_data['results']['parents']# json文件中的列表
for each in comment_list:
print(each["content"])# 列表的content字段
上面我们把第一页的评论爬取出来了,如果人工找每一页的真实地址会非常的麻烦,因此我们可以根据规律使用for循环爬取
我们可以发现第一页的真实地址是:https://api-zero.livere.com/v1/comments/list?callback=jQuery1124024945189329458972_1609143449063&limit=10&offset=1&repSeq=4272904&requestPath=%2Fv1%2Fcomments%2Flist&consumerSeq=1020&livereSeq=28583&smartloginSeq=5154&code=&_=1609143449073
第二页的真实地址是:https://api-zero.livere.com/v1/comments/list?callback=jQuery11240527837944980206_1609142567812&limit=10&offset=3&repSeq=4272904&requestPath=%2Fv1%2Fcomments%2Flist&consumerSeq=1020&livereSeq=28583&smartloginSeq=5154&code=&_=1609142567814
可以发现URL地址中有两个比较重要的变量,limit代表每一页评论数量的最大值,offset表示评论页
import requests
import lxml.html
import json
url='https://api-zero.livere.com/v1/comments/list?callback=jQuery112404090822790751196_1609138618610&limit=10&repSeq=4272904&requestPath=%2Fv1%2Fcomments%2Flist&consumerSeq=1020&livereSeq=28583&smartloginSeq=5154&code=&_=1609138618612'
headers={
'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Mobile Safari/537.36'}
def single_page_comment(url,i):
r=requests.get(url,headers=headers)
print(r.text)
open('html'+str(i)+'.txt','w',encoding='utf-8').write(r.text)
for i in range(5):
link1='https://api-zero.livere.com/v1/comments/list?callback=jQuery11240527837944980206_1609142567812&limit=10&offset='
link2='&repSeq=4272904&requestPath=%2Fv1%2Fcomments%2Flist&consumerSeq=1020&livereSeq=28583&smartloginSeq=5154&code=&_=1609142567814'
url=link1+str(i+1)+link2
single_page_comment(url,i+1)
# https://api-zero.livere.com/v1/comments/list?callback=jQuery1124024945189329458972_1609143449063&limit=10&offset=1&repSeq=4272904&requestPath=%2Fv1%2Fcomments%2Flist&consumerSeq=1020&livereSeq=28583&smartloginSeq=5154&code=&_=1609143449073
# https://api-zero.livere.com/v1/comments/list?callback=jQuery11240527837944980206_1609142567812&limit=10&offset=3&repSeq=4272904&requestPath=%2Fv1%2Fcomments%2Flist&consumerSeq=1020&livereSeq=28583&smartloginSeq=5154&code=&_=1609142567814
在之前的例子中,使用Chrome 的“检查”功能找到源地址十分容易,但是有一些网站非常复杂,如天猫产品评论,使用“检查”功能很难找到调用的网页地址。除此之外,有一些数据真实地址的URL也十分冗长和复杂,有些网站为了规避这些抓取会对地址进行加密,造成其中的一些变量让人摸不着头脑。
因此,这里介绍另一种方法,即使用浏览器渲染引擎。直接用浏览器在显示网页时解析HTML、应用CSS样式并执行JavaScript的语句。这个方法在爬虫过程中会打开一个浏览器加载该网页,自动操作浏览器浏览各个网页,顺便把数据抓下来。用- -句简单而通俗的话说,就是使用浏览器渲染方法将爬取动态网页变成爬取静态网页。
我们可以用Python的Selenium库模拟浏览器完成抓取。Selenium 是一个用于Web应用程序测试的工具。Selenium测试直接运行在浏览器中,浏览器自动按照脚本代码做出单击、输入、打开、验证等操作,就像真正的用户在操作一样。
pip install selenium
下载ChromeDriver,根据自己的系统下载合适的版本,下载地址
from selenium import webdriver
driver=webdriver.Chrome()
driver.get('baidu.com')
使用代码模拟浏览器打开baidu.com
,我们会发现会报错,这时因为在同文件文件路径中不存在ChromeDriver.exe
文件
webdriver.Chrome()函数的参数为ChromeDriver.exe
文件的路径,我们也可以把该文件复制到同路径下
正常情况下,右键点击“检查”,其中的Elements
选择就是浏览器渲染的成的静态网页代码
from selenium import webdriver
import time
driver=webdriver.Chrome()
driver.get('https://m.weibo.cn/detail/4467107636950632')
time.sleep(5)
html=driver.page_source
open('html.txt','w',encoding='utf-8').write(html)
print(html)
comment=driver.find_elements_by_xpath('//*[@id="app"]/div[1]/div/div[4]/div[2]/div/div/div/div/div/div[2]/div[1]/div/div/h3')
for each in comment:
content=each.text
print(content)
Selenium中常见的操作元素方法
from selenium import webdriver
f = webdriver.FirefoxProfile()
f.set_preference("permissions.default.stylesheet",2)
driver = webdriver.Firefox(firefox_profile = f)
driver.get('https://www.douban.com/')
from selenium import webdriver
f = webdriver.FirefoxProfile()
f.set_preference("permissions.default.image",2)
driver = webdriver.Firefox(firefox_profile = f)
driver.get('https://www.douban.com/')
from selenium import webdriver
f = webdriver.FirefoxProfile()
f.set_preference("javascript.enabled",False)
driver = webdriver.Firefox(firefox_profile = f)
driver.get('https://www.douban.com/')
from selenium import webdriver
import time
url='https://www.airbnb.cn/s/%E6%B7%B1%E5%9C%B3%E5%B8%82/homes?refinement_paths%5B%5D=%2Fhomes&screen_size=large&checkin=2020-12-28&checkout=2020-12-29&host_promotion_type_ids%5B%5D=1&host_promotion_type_ids%5B%5D=8'
driver=webdriver.Chrome()
driver.get(url)
def driver_run(driver):
time.sleep(5)
comment=driver.find_elements_by_xpath('/html/body/div[3]/main/div/div/div/div[3]/div/div/section/div/div/div[2]/div/div/div[2]/div/div/div/div/div')
for each in comment:
name=each.find_element_by_xpath('div/div/div/div/div/div/div/div[2]/div/div/a/div[1]/div[2]/div/div').text
print(name)
driver_run(driver)
for i in range(0,14):
driver.find_element_by_xpath('//*[@id="site-content"]/div/div/div/div[3]/div/div/div/div[1]/div[1]/a[6]/svg/g/path').click()
driver_run(driver)
爬取农贸市场所有商品名称与价格
from selenium import webdriver
import time
url='http://www.gznw.com/eportal/ui?pageId=595091'
driver=webdriver.Chrome()
driver.get(url)
def driver_run(driver):
time.sleep(5)
comment=driver.find_elements_by_xpath('//*[@id="5c96d136291949729295e25ea7e708b7"]/div[2]/div[2]/table/tbody/tr')
for each in comment:
name=each.find_element_by_xpath('td[2]').text
price=each.find_element_by_xpath('td[4]').text
print(name," ",price)
driver_run(driver)
for i in range(0,2014):
driver.find_element_by_xpath('//*[@id="5c96d136291949729295e25ea7e708b7"]/div[2]/div[3]/table/tbody/tr/td/a[3]').click()
driver_run(driver)