目前,全球有四分之三的网站会通过JavaScript动态生成其内容,这意味着我们通过f12,无法在HTML中查看到对于内容。

为了解决这个问题,一般有两种方案

  1. JavaScript逆向工程
  2. 渲染JavaScript获得渲染后的内容

JavaScript逆向工程

所谓JavaScript逆向工程就是找到Ajax技术动态获取数据的接口,我们以"360图片"网站为例,在浏览器中输入https://image.so.com/z?ch=wallpaper就可以打开"360图片"的"壁纸板块"。

我们发现,页面上的图片都是通过JavaScript代码异步获取Json数据并动态渲染而成的,而且整个页面使用了瀑布式加载(一边向下滚动,一边加载更多图片)。在"开发者工具"中可以找到提供动态内容的接口,如下图所示:

image-20211227113639559

所以我们可以请求如下的URL,其中ch代表频道,sn代表页码:

https://image.so.com/zjl?ch=wallpaper&sn=0 # 第一页
https://image.so.com/zjl?ch=wallpaper&sn=30 # 第二页

比如我们去去请求第二页:

image-20211227113933591

我们就会得到对应的JSON文件。

使用Selenium

尽管很多网站对自己的网络API接口进行了保护,增加了获取数据的难度,但是只要经过足够的努力,绝大多数还是可以被逆向工程的,但是在实际开发中,我们可以通过浏览器渲染引擎来避免这些繁琐的工作,WebKit就是一个利用的渲染引擎。

WebKit的代码始于1998年的KHTML项目,当时它是Konqueror浏览器的渲染引擎。2001年,苹果公司从这个项目的代码中衍生出了WebKit并应用于Safari浏览器,早期的Chrome浏览器也使用了该内核。在Python中,我们可以通过Qt框架获得WebKit引擎并使用它来渲染页面获得动态内容。

如果没有打算用上面所说的方式来渲染页面并获得动态内容,其实还有一种替代方案就是使用自动化测试工具Selenium,它提供了浏览器自动化的API接口,这样就可以通过操控浏览器来获取动态内容。首先可以使用pip来安装Selenium。

pip install selenium

使用selenium还要下载不同的浏览器驱动

Firefox浏览器驱动:geckodriver

Chrome浏览器驱动:chromedriver , taobao备用地址

IE浏览器驱动:IEDriverServer

Edge浏览器驱动:MicrosoftWebDriver

Opera浏览器驱动:operadriver

PhantomJS浏览器驱动:phantomjs

需要把浏览器驱动放入系统路径中,或者直接告知selenuim的驱动路径

另外启动浏览器可以设置i一些参数,比如无界面等,参考:here

测试是否可以正常使用:

from selenium import webdriver
from selenium.webdriver.chrome.options import Options

driver = webdriver.Chrome() # 加载驱动器

options = Options()
options.binary_location = "C:\Program Files\Google\Chrome\Application\chrome.exe" # 本地浏览器路径

driver.get('https://blog.ikedong.cn') # 访问的url

结果:

image-20211227145128181

元素定位

find_element_by_id()
find_element_by_name()
find_element_by_class_name()
find_element_by_tag_name()
find_element_by_link_text()
find_element_by_partial_link_text()
find_element_by_xpath() # 通过Xpath定位
find_element_by_css_selector() # 通过css选择器定位

其中,把element变成elements就是找所有满足的条件,返回一个数组

控制浏览器操作

driver.set_window_size(480,800) # 控制浏览器大小
driver.back() # 浏览器后退
driver.forward() # 浏览器前进
driver.refresh() # 刷新

webelement常用方法

点击和输入

driver.find_element_by_id("kw").clear() # 清除文本
driver.find_element_by_id("kw").send_keys("selenium") # 模拟按键输入
driver.find_element_by_id("su").click() # 单击元素

提交

search_text = driver.find_element_by_id("kw")
search_text.send_keys("selenium")
search_text.submit()
# 模拟搜索框回车操作

其他

size # 返回元素尺寸
text # 获取元素文本
get_attribute(name) # 获取属性值
is_displayed() # 设置该元素是否用户可见

鼠标操作

在WebDriver中,将关于鼠标操作的方法都封装在了ActionChains类中,其中常用方法有:

perform() # 执行所有ActionChains中存储的行为
click # 左击
click_and_hold # 左击并不松开
context_click() # 右击
double_click() # 双击
drag_and_drop(source,target) # 拖动到某元素松开
drag_and_drop_by_offset(source,xoffset,yoffset) # 拖动到某个坐标后松开
move_to_element() # 鼠标悬停
key_down(value) # 按下某个键
key_up(value) # 松开某个键

Demo

现在实现一个简单的功能,模拟鼠标悬停到'笔记',然后点击'笔记'下的'code'

import time
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.action_chains import ActionChains

driver = webdriver.Chrome() 
options = Options()
options.binary_location = "C:\Program Files\Google\Chrome\Application\chrome.exe"
driver.get('https://blog.ikedong.cn')

# 定位要悬停的元素'笔记'
above = driver.find_element_by_xpath('//*[@id="menu-menu"]/li[1]/a')
# 鼠标悬停至'笔记'
ActionChains(driver).move_to_element(above).perform()
# 定位要点击的元素'code'
click = driver.find_element_by_xpath('//*[@id="menu-menu"]/li[1]/ul/li[2]/a')
# 鼠标点击'code'
ActionChains(driver).click(click).perform()

ActionChainsDemo

键盘事件

send_keys(Keys.BACK_SPACE) # 删除键(BackSpace)
send_keys(Keys.SPACE) # 空格键(Space)
send_keys(Keys.TAB) # 制表键(Tab)
send_keys(Keys.ESCAPE) # 回退键(Esc)
send_keys(Keys.ENTER) # 回车键(Enter)
send_keys(Keys.CONTROL,'a') # 全选(Ctrl+A)
send_keys(Keys.CONTROL,'c') # 复制(Ctrl+C)
send_keys(Keys.CONTROL,'x') # 剪切(Ctrl+X)
send_keys(Keys.CONTROL,'v') # 粘贴(Ctrl+V)
send_keys(Keys.F1) # 键盘 F1

Demo

现在实现一个简单的功能,在搜索框中输入'pythonn',然后再删除多余的'n',最后搜索。

import time
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys

driver = webdriver.Chrome() 
options = Options()
options.binary_location = "C:\Program Files\Google\Chrome\Application\chrome.exe"
driver.get('https://blog.ikedong.cn')

# 点击搜索图标
search = driver.find_element_by_xpath('/html/body/header/div/div[3]/i')
ActionChains(driver).click(search).perform()
time.sleep(1)
# 在文本框中输入'pythonn'
driver.find_element_by_xpath('/html/body/form/div[1]/div/input').send_keys('pythonn')
time.sleep(1)
# 删除文本框中多余的'n'
driver.find_element_by_xpath('/html/body/form/div[1]/div/input').send_keys(Keys.BACK_SPACE)
time.sleep(1)
# 回车搜索
driver.find_element_by_xpath('/html/body/form/div[1]/div/input').send_keys(Keys.ENTER)

ActionChainsDemo