一、动态网页爬取介绍
在许多网站中,都使用javascript编写网站,很多内容不会出现在HTML源代码中,所以不能使用之前爬取静态网页的方法。有一种异步技术叫AJAX,它的价值在于通过后台与服务器进行少量数据变换就可以使网页实现异步更新。也就是说在不重新加载整个网页的情况下对网页的某部分进行更新。减少网页重复内容的下载,节省流量。但是随之带来的麻烦就是,我们不能再HTML代码中找到我们想要的数据。要想解决这种爬取动态加载的内容,有以下两种方法:
- 通过浏览器审查元素解析地址
- 通过Selenium模拟浏览器抓取
二、解析真实地址抓取
我们想要的数据没有在HTML中,但是可以找到数据的真实地址,请求这个地址也可以找到想要的数据,同样完成书上的例子。首先打开网站:http://www.santostang.com/2018/07/04/hello-world/,这个网站的就是使用JS写的,右击鼠标,点击检查。刷新页面,可以看到加载了网页所有的文件,这个过程是抓包的过程,然后,点解network找到包含数据的文件,记住是json数据。
点击Headers,找到真实地址。然后 想真实地址发送请求。
1. 发送请求,获取数据
import requests
# 构建url
url = """https://api-zero.livere.com/v1/comments/list?callback=jQuery1124006805677727422466_1546902786995&limit=10&repSeq=4272904&requestPath=%2Fv1%2Fcomments%2Flist&consumerSeq=1020&livereSeq=28583&smartloginSeq=5154&_=1546902786997"""
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'}
r = requests.get(url, headers=headers)
print(r.text)
2. 解析数据
从json数据中提取评论,首先我们要找到符合json格式的部分。然后,使用json.loads把字符串格式的响应体转化为json数据。然后,根据json数据的结构,可以提取评论的列表comment_list.最后通过for循环,提取评论文本,输出打印。
import json
json_string = r.text
# 找到符合json格式的数据
json_string = json_string[json_string.find('{'): -2]
# 转换为json格式
json_data = json.loads(json_string)
comment_list = json_data['results']['parents']
for eachone in comment_list:
message = eachone['content']
print(message)
三、通过selenium模拟浏览器抓取
有些网站会对地址进行加密,使得一些变量让人分析不清楚。接下来,我们就使用第二种方法,使用浏览器渲染引擎。直接用浏览器在显示网页时解析HTML,应用CSS样式并执行JavaScript的语句。这个方法会打开一个浏览器,加载该网页,自动操作浏览器浏览各个网页,顺便抓取数据,将爬取动态网页变为爬取静态网页。我们可以使用selenium库模拟浏览器完成抓取。Selenium是一个用于Web应用程序测试的工具。直接运行在浏览器中,浏览器自动按照脚本代码做出点击、输入、打开、验证等操作。
1. 安装selenium
pip install selenium
下载‘geckodriver’并在Python的安装目录中。
from selenium import webdriver
from selenium.webdriver.firefox.firefox_binary import FirefoxBinary
# 通过selenium启动博客网站
caps = webdriver.DesiredCapabilities().FIREFOX
caps["marionette"] = True
binary = FirefoxBinary(r'C:\Program Files\Mozilla Firefox\firefox.exe')
driver = webdriver.Firefox(firefox_binary=binary, capabilities=caps)
driver.get("http://www.santostang.com/2018/07/04/hello-world/")
2. 使用selenium爬取数据
第一步:找到评论的HTML代码标签。使用火狐浏览器,点击页面,找到标签,定位评论数据
第二步:尝试 获取一条评论数据,使用如下代码获取数据。
# 使用CSS选择器查找元素,找到class为'reply-content'的div元素
comment = driver.find_element_by_css_selector('div.reply-content')
# 通过元素的tag去寻找‘p’元素
content = comment.find_element_by_tag_name('p')
print(content.text)
还是有问题啊 网页打不开啊
还有可能存在解析不出来报错的情况,报“Message:Unable to locate element:div.reply_content”的错。这是因为源代码需要解析成一个iframe,则需要添加如下一行代码。
driver.switch_to.frame(driver.find_element_by_css_selector("iframe[title='livere']"))
完整的爬取评论代码:
from selenium import webdriver
from selenium.webdriver.firefox.firefox_binary import FirefoxBinary
import time
# 通过selenium启动博客网站
caps = webdriver.DesiredCapabilities().FIREFOX
caps["marionette"] = True
binary = FirefoxBinary(r'C:\Program Files\Mozilla Firefox\firefox.exe')
driver = webdriver.Firefox(firefox_binary=binary, capabilities=caps)
driver.get("http://www.santostang.com/2018/07/04/hello-world/")
driver.switch_to.frame(driver.find_element_by_css_selector("iframe[title='livere']"))
comments = driver.find_elements_by_css_selector('div.reply-content')
for eachcomment in comments:
content = eachcomment.find_element_by_tag_name('p')
print(content.text)
selenium还有许多其他的应用:
- find_element_by_id:通过元素的id选择,例如:driver.find_element_by_id('loginForm')
- find_element_by_name:通过元素的name选择,driver.find_element_by_name('password')
- find_element_by_xpath:通过xpath选择,driver.find_element_by_xpath("//form[1]")
- find_element_by_link_text:通过链接地址选择
- find_element_by_partial_link_text:通过链接的部分地址选择
- find_element_by_tag_name:通过元素的名称选择
- find_element_by_class_name:通过元素的id选择
- find_element_by_css_selector:通过css选择器选择
在element中加上s,就能选择多个元素。selenium操作元素方法实现自动操作网页。常见的操作元素方法如下:
- clear 清除元素的内容
- send_keys 模拟按键输入
- click 点击元素
- submit 提交表单
使用selenium自动登录程序中的一部分。
user = driver.find_element_by_name("username") # 找到用户名输入框
user.clear # 清楚用户没那个输入框内容
user.send_keys("1234") # 输入用户名
pwd = driver.find_element_by_name("password") # 找到密码输入框
pwd.clear # 清楚密码输入框内容
pwd.send_keys("123456") # 在框中输入密码
driver.find_element_by_id("loginBtn").click() # 点击登录
3. selenium的高级操作
selenium的工作原理就是把整个网页加载出来,将动态网页转换为静态网页,在爬取内容。这样的一个缺点就是速度慢。
下面是用selenium控制浏览器加载内容,从而加快爬取速度。
1)控制CSS的加载:抓取过程中,我们只抓取页面内容,不需要加载CSS样式。我们就可以限制加载css样式。
from selenium import webdriver
from selenium.webdriver.firefox.firefox_binary import FirefoxBinary
# 通过selenium启动博客网站
caps = webdriver.DesiredCapabilities().FIREFOX
caps["marionette"] = True
binary = FirefoxBinary(r'C:\Program Files\Mozilla Firefox\firefox.exe')
fp = webdriver.FirefoxProfile()
fp.set_preference("permissions.default.stylesheet", 2)
driver = webdriver.Firefox(firefox_binary=binary, firefox_profile=fp, capabilities=caps)
driver.get("http://www.santostang.com/2018/07/04/hello-world/")
2) 限制图片的加载,如果我们不爬取图片的话,我们就可以禁止图片的加载。
from selenium import webdriver
from selenium.webdriver.firefox.firefox_binary import FirefoxBinary
# 通过selenium启动博客网站
caps = webdriver.DesiredCapabilities().FIREFOX
caps["marionette"] = True
binary = FirefoxBinary(r'C:\Program Files\Mozilla Firefox\firefox.exe')
fp = webdriver.FirefoxProfile()
fp.set_preference("permissions.default.image", 2)
driver = webdriver.Firefox(firefox_binary=binary, firefox_profile=fp, capabilities=caps)
driver.get("http://www.santostang.com/2018/07/04/hello-world/")
3) 限制JavaScript的执行。如果抓取的内容不是通过JavaScript得到的。那就可以禁止其执行
from selenium import webdriver
from selenium.webdriver.firefox.firefox_binary import FirefoxBinary
# 通过selenium启动博客网站
caps = webdriver.DesiredCapabilities().FIREFOX
caps["marionette"] = True
binary = FirefoxBinary(r'C:\Program Files\Mozilla Firefox\firefox.exe')
fp = webdriver.FirefoxProfile()
fp.set_preference("javascript.enabled", False)
driver = webdriver.Firefox(firefox_binary=binary, firefox_profile=fp, capabilities=caps)
driver.get("http://www.santostang.com/2018/07/04/hello-world/")
四、selenium爬虫实践:深圳短租数据
from selenium import webdriver
from selenium.webdriver.firefox.firefox_binary import FirefoxBinary
import time
caps = webdriver.DesiredCapabilities().FIREFOX
caps["marionette"] = True
binary = FirefoxBinary(r'C:\Program Files\Mozilla Firefox\firefox.exe')
driver = webdriver.Firefox(firefox_binary=binary, capabilities=caps)
driver.get("https://zh.airbnb.com/s/Shenzhen--China/homes?refinement_paths%5B%5D=%2Fhomes&click_referer=t%3ASEE_ALL%7Csid%3Aa5f17a7d-ef69-448d-bab8-ae041dc4ac46%7Cst%3AMAGAZINE_HOMES&title_type=MAGAZINE_HOMES&query=Shenzhen%2C%20China&allow_override%5B%5D=&s_tag=TTtd9f9M§ion_offset=6&items_offset=18")
# 获取全部数据标签
rent_list = driver.find_elements_by_css_selector('div._v72lrv')
for eachhouse in rent_list:
name = eachhouse.find_element_by_css_selector('div._vbshb6')
name = name.text
print(name)
这个案例,我只实现了最简单的爬取一个页面,具体的查找方法,可以参考书中的方法,但是网页有变化,我们要学习方法,而不是照抄。
注意:本篇学习笔记,是总结唐松老师的《Python网络爬虫从入门到实践》这本书的内容,如果想了解书中详细内容,请自行购买。