Python爬虫,使用selenium爬取动态生成的网页数据 - 旧人笔记 - 旧人学习笔记 - 爬虫笔记 - 网络爬虫大白话

什么是AJAX

AJAX(Asynchronouse JavaScript And XML)异步JavaScript和XML,在后台与服务器进行少量数据交换,Ajax可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。传统的网页(不使用Ajax)如果需要更新内容,必须重载整个网页页面。

因为传统的在传输数据格式方面,使用的是XML语法,因此叫做AJAX,其实现在数据交互基本上都是使用JSON,很少使用XML了。

使用AJAX加载的数据,即使执行了JS代码 将数据渲染到了浏览器中,在右键-查看网页源代码还是不能看到通过ajax加载的数据。

查看源代码看到的是服务器最原始的HTML,不包含ajax生成的HTML元素。

如下图:可以看到页面中引入了很多js文件和css文件,这些文件有的是用来动态生成元素的,有的是其他用途。反正除了这些文件路径和一些JavaScript代码,在页面中获取不到任何有用的网页信息。

Python爬虫,使用selenium爬取动态生成的网页数据 - 旧人笔记 - 旧人学习笔记 - 爬虫笔记 - 网络爬虫大白话_第1张图片

而审查元素则可以看到生成的各种HTML元素,这些元素都是浏览器通过对引入的js和css进行解析生成的,生成这些元素使用的是ajax技术。

如下图:

Python爬虫,使用selenium爬取动态生成的网页数据 - 旧人笔记 - 旧人学习笔记 - 爬虫笔记 - 网络爬虫大白话_第2张图片


获取ajax数据的方式

1、直接分析ajax调用的接口,然后通过代码请求这个接口,俗称怼加密(找加密、网页js逆向),需要很强的前端 JavaScript功底。

优点:直接可以请求到数据,不需要做一些解析工作,代码量少,性能高
缺点:分析接口比较复杂,特别是一些通过js混淆的接口,要有一定的js功底;容易被发现是爬虫

2、使用Selenium+chromedriver模拟浏览器行为获取数据,使用代码模拟人工操作,用代码控制浏览器,得到的数据是和审查元素一样的,也就是说浏览器上能看到的都能得到。

优点:直接模拟浏览器的行为,浏览器能请求到的,使用selenium也能请求到;爬虫更稳定
缺点:代码量多,性能低

一般在post请求的时候,比如注册或登陆时,提交数据经常是加密的(一般密码会进行加密),这种都是需要找出加密方法,并且在post请求的时候模拟出加密数据。

随着慢慢普及,现在js逆向几乎每个人都会一些,只是有些js混淆的厉害,着实难搞。


Selenium+chromedriver获取动态数据

Selenium相当于是一个机器人,可以模拟人类在浏览器上的一些行为,自动处理浏览器上的一些行为,比如点击,填充数据,删除cookie等。

chromedriver是一个驱动Chrome浏览器的驱动程序,使用它才可以驱动浏览器。
当然,针对不同的浏览器有不同的driver。

Chrome:https://sites.google.com/a/chromium.org/chromedriver/downloads
Firefox:https://github.com/mozilla/geckodriver/releases
Edge:https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/
Safari:https://webkit.org/blog/6900/webdriver-support-in-safari-10/

安装Selenium和chromedriver

安装Selenium:
Selenium有很多语言的版本,java、ruby、python等。

python版本安装:

pip install selenium

安装chromedriver:
通过上面的地址,下载完成后,放到不需要权限的纯英文目录下就可以了。

快速入门

一个获取百度首页的例子

from selenium import webdriver

# chromedriver所在的绝对路径
driver_path = r'D:\ProgramApp\chromedriver\chromedriver.exe'

# 使用webdriver.Chrome初始化一个对象(driver),并且指定chromedriver的路径
driver = webdriver.Chrome(executable_path=driver_path)

driver.get("https://www.baidu.com/")   # 请求网页
print(driver.page_source)  # 通过page_source获取网页源代码

selenium常用操作

更多教程请参考:http://selenium-python.readthedocs.io/installation.html#introduction

关闭页面

driver.close():关闭当前页面
driver.quit():退出整个浏览器

定位元素

1、根据id查找元素

submitTag = driver.find_element_by_id('su')

# 另一种方式
from selenium.webdriver.common.by import By          # 导入By模块
submitTag1 = driver.find_element(By.ID,'su')

2、根据类名查找元素

submitTag = driver.find_element_by_class_name('su')

# 另一种方式
from selenium.webdriver.common.by import By          # 导入By模块
submitTag1 = driver.find_element(By.CLASS_NAME,'su')

3、根据name属性的值查找元素

submitTag = driver.find_element_by_name('email')

# 另一种方式
from selenium.webdriver.common.by import By          # 导入By模块
submitTag1 = driver.find_element(By.NAME,'email')

4、根据标签名查找元素

submitTag = driver.find_element_by_tag_name('div')

# 另一种方式
from selenium.webdriver.common.by import By          # 导入By模块
submitTag1 = driver.find_element(By.TAG_NAME,'div')

5、根据xpath语法获取元素

submitTag = driver.find_element_by_xpath('//div')

# 另一种方式
from selenium.webdriver.common.by import By          # 导入By模块
submitTag1 = driver.find_element(By.XPATH,'//div')

6、根据css选择器选择元素

submitTag = driver.find_element_by_css_selector('.quickdelete-wrap > input')

# 另一种方式
from selenium.webdriver.common.by import By          # 导入By模块
submitTag1 = driver.find_element(By.CSS_SELECTOR,'.quickdelete-wrap > input')

注意:
find_element是获取第一个满足条件的元素
find_elements是获取所有满足条件的元素,返回列表

如果只是解析获取网页中的数据,可使用driver.page_source获取网页HTML源代码,再将网页代码用lxml解析,因为lxml底层是用c语言执行的,效率高。
而且使用这种方式,只需要打开一次页面获取到HTML源代码后就可以了,使用selenium获取元素的方法有时候还获取不到文本和标签属性的值(使用xpath中的 /text()、/@属性 的时候)。

使用driver.page_source获取网页HTML源代码后,还可以使用正则、bs4等其他方法解析数据。

如果需要对网页中的一些元素进行一些操作,如:给文本框输入内容、点击按钮等,就必须使用selenium提供的查找元素方法来操作。

操作表单元素

常见的表单元素(更多可自行学习HTML知识):

input标签:
文本框:type=“text”/“password”/“email”/“number” ---- typed等于这些的都属于文本框
text 是普通文本框,password是密码输入框,number是数字

button(按钮):input type=“submit” ---- button也属于input标签,typed=“submit”

checkbox(选择框):input type=“checkbox” ---- checkbox也属于input标签,type=“checkbox”

select(下拉列表)

1、 操作输入框:
第一步:找到元素
第二步:使用send_keys(value),将数据填充进去

inputTag = driver.find_element_by_id('kw')
inputTag.send_keys('python')

# 使用clear方法可以清除输入框中的内容
inputTag.clear()

2、操作checkbox:
因为要选中checkbox标签,在网页中是通过鼠标左键点击的,所以先选中这个标签,再执行click事件

rememberTag = driver.find_element_by_name("rememberMe")
rememberTag.click()

rememberBth.click()   # 再点一次取消选中选择框

3、选择select:
select元素不能直接点击,因为点击后还需要选中元素,这时候selenium就专门为select标签提供了一个类selenium.webdriver.support.ui.Select

将获取到的元素当成参数传到这个类中,创建这个对象,以后就可以使用这个对象进行选择了

from selenium.webdriver.support.ui import Select
# 选中这个标签,然后使用Select创建对象
selectTag = Select(driver.find_element_by_id('jk'))

selectTag.select_by_index(1)    # 根据索引选择
selectTag.select_by_value("value属性的值")   # 根据value值选择
selectTag.select_by_visible_text("标签中的文本")  # 根据可视的文本选择

selectTag.deselect_all()    # 取消选中所有选项

4、操作按钮 button:
操作按钮有很多种方式,比如单击、右击、双击等。最常用的就是点击,直接调用click方法就可以了

inputTag = driver.find_element_by_id('su')
inputTag.click()

按钮单击跟选择框一样都使用.click()

行为链

有时候在页面中的操作可能要有很多步,这时可以使用鼠标行为链类ActionChains来完成。比如现在要将鼠标移动到某个元素上并执行点击事件。

from selenium.webdriver.common.action_chains import ActionChains
driver.get('https://www.baidu.com')             # 模拟打开百度页面

inputTag = driver.find_element_by_id('kw')      # 获取到百度输入框
submitTag = driver.find_element_by_id('su')     # 获取到百度一下按钮
# 选中这两个元素后再进行下面的行为链ActionChains操作

actions = ActionChains(driver)       # 创建行为链对象,把driver传进去
actions.move_to_element(inputTag)    # 把鼠标移动到inputTag标签上
actions.send_keys_to_element(inputTag,'python')   # 给inputTag发送数据(要搜索的关键词:‘python’)
actions.move_to_element(submitTag)   # 再把鼠标移动到submitBth标签上
actions.click(submitTag)             # 点击submitBth按钮
actions.perform()                    # 停止行为

更多的鼠标相关的操作

click_and_hold(element):点击但不松开鼠标。
context_click(element):右键点击。
double_click(element):双击。

更多方法请参考:http://selenium-python.readthedocs.io/api.html

Cookie操作

获取所有的cookie:

for cookie in driver.get_cookies():
    print(cookie)

根据cookie的key获取value:

value = driver.get_cookie(key)

删除所有的cookie:

driver.delete_all_cookies()

删除某个cookie:

driver.delete_cookie(key)


driver.delete_cookie('PSTM')
print(driver.get_cookie('PSTM'))   # 删除后就获取不到这条cookie了,返回None

页面等待

现在的网页越来越多采用了 Ajax 技术,这样程序便不能确定何时某个元素完全加载出来了。如果实际页面等待时间过长导致某个dom元素还没出来,但代码直接使用了这个WebElement,那么就会抛出NullPointer的异常。

为了解决这个问题,所以 Selenium 提供了两种等待方式:一种是隐式等待、一种是显式等待。

1、隐式等待:调用driver.implicitly_wait。这时在获取元素之前,会先等待

在创建driver时,为浏览器对象创建一个最长等待时间,这个方法是得不到某个元素就等待,直到浏览器所有元素加载完毕(左上角的圈圈不在转了)、拿到元素位置(如果一直拿不到就等到时间截止),再执行下一步

隐式等待是全局的,设置一次后,所有的元素在加载的时候都会等待,会在那死等,等不到(超时)就抛出异常然后继续执行后面的代码

driver.implicitly_wait(10)  # 10秒

# 请求网页
driver.get("https://www.douban.com")

driver.find_element_by_id('lalala')
# 输入一个错误的id,不设置等待的话会立马抛出异常,设置等待会等待后抛出异常

2、显示等待:显示等待是明确表明某个条件成立后才执行获取元素的操作,可以在等待的时候指定一个最大的时间,如果超过这个时间就抛出一个异常

显示等待应该使用selenium.webdriver.support.excepted_conditions期望的条件和selenium.webdriver.support.ui.WebDriverWait来配合完成

显式等待是根据条件来判断,条件满足就不等了,条件一直不满足就一直等待到超时并抛出异常

WebDriverWait(driver, timeout, poll_frequency=0.5, ignored_exceptions=None)
——driver:WebDriver 的驱动程序(Ie, Firefox, Chrome 或远程)
——timeout:最长超时时间,默认以秒为单位
——poll_frequency:休眠时间的间隔(步长)时间,默认为 0.5 秒
——ignored_exceptions:超时后的异常信息,默认情况下抛 NoSuchElementException 异常

WebDriverWait(driver, 10).until(
    lambda driver: driver.findElement(By.Id("someId")))  # 用这种方式也可以指定期望的条件
# 每隔0.5秒扫描一次检查是否有id为someId的元素,10秒后没找到就不找了抛异常
from selenium.webdriver.common.by import By   # 另一种获取元素的方法  By类
from selenium.webdriver.support.ui import WebDriverWait  # WebDriverWait 显示等待的类
from selenium.webdriver.support import expected_conditions as EC  # 期望条件,太长了所以一般取别名为EC

try:
    element = WebDriverWait(driver, 10).until(   # 传入driver和最大等待时间
        EC.presence_of_element_located((By.ID, 'form_email'))  # 期望的条件(By.ID, 'form_email')
    )
finally:
    driver.quit()

期望的条件只能传递一个参数,所以两个参数要放元组里

.presence_of_element_located():判断某个元素是否存在当前页面
有form_email这个id表示条件满足,说明要寻找的页面的元素已经加载出来了

更多等待条件:

presence_of_element_located:某个元素已经加载完毕了
presence_of_all_emement_located:网页中所有满足条件的元素都加载完毕了
element_to_be_cliable:某个元素是可以点击了

更多条件请参考:http://selenium-python.readthedocs.io/waits.html

切换页面

有时候窗口中有很多子tab页面,这时肯定是需要进行切换的

selenium提供了driver.execute_script(“window.open(‘url’)”)方法来打开一个新的tab页面

driver.switch_to_window()方法来进行切换,具体切换到哪个页面,可以用driver.window_handles()来实现

# 打开一个新的页面
driver.execute_script("window.open('url')")
# 切换到这个新的页面中
driver.switch_to_window(driver.window_handles[1])
from selenium import webdriver


driver_path = r'D:\PycharmProjects\chromedriver\chromedriver.exe'
# 定义chromedriver.exe所在的绝对路径

driver = webdriver.Chrome(executable_path=driver_path)
# 使用webdriver.Chrome初始化一个对象(driver),驱动的是Chrome浏览器,指定上面创建路径。


driver.get('https://www.baidu.com')                  # 模拟打开百度页面
# driver.get('https://www.douban.com')
# 模拟打开豆瓣网页面,用这种方式打开多个网址都是在浏览器的一个tab标签页面下的,所以会覆盖前面打开的页面


# 打开一个新的页面
driver.execute_script("window.open('https://www.douban.com')")   # window.open是js里面的方法
print(driver.current_url)
# 虽然在新的tab标签页面下打开了豆瓣网页,但是driver当前所在页面url还是百度
# 如果想要在代码中切换到某个页面并做一些爬虫操作,就需要用到.switch_to_window()来进行切换
# 通过.window_handles取出具体第几个窗口
# .window_handles是一个列表,里面放的是窗口句柄,按照窗口打开时候的顺序进行并排序的


# 切换到这个新的页面中
print(driver.window_handles)        # 查看当前打开的浏览器所有的标签页面窗口句柄

driver.switch_to_window(driver.window_handles[1])  # 切换tab页面
print(driver.current_url)           # 现在的url是豆瓣的了
# print(driver.page_source)           # 通过page_source获取网页源代码。打印出来的是豆瓣的页面HTML代码

driver.switch_to.window(driver.window_handles[0])
print(driver.current_url)           # 现在的url又是百度的了

.switch_to_window()是老版本,被废弃了,会提现下面的错误,但是不影响运行

DeprecationWarning: use driver.switch_to.window instead
self.driver.switch_to_window(self.driver.window_handles[1])

新的版本:.switch_to.window()

设置代理ip

有时候频繁爬取一些网页,服务器发现你是爬虫后会封掉你的ip地址,这时候我们可以更改代理ip

更改代理ip,不同的浏览器有不同的实现方式,这里仅以Chrome浏览器为例

from selenium import webdriver

driver_path = r'D:\PycharmProjects\chromedriver\chromedriver.exe'

options = webdriver.ChromeOptions()    # .ChromeOptions类是设置Chrome浏览器一些请求信息的
options.add_argument('--proxy-server=http://27.203.219.181:8118')   # 在请求信息里设置代理服务器ip

driver = webdriver.Chrome(executable_path=driver_path, chrome_options=options)
# 使用webdriver.Chrome初始化一个对象,指定上面创建路径和代理ip

driver.get('http://httpbin.org/ip')

WebElement元素

from selenium.webdriver.remote.webelement import WebElement类是每个获取出来的元素的所属类

有一些常用的方法 属性:

1、get_attribute():这个标签的某个属性的值
2、screentshot():获取当前页面的截图,这个方法只能在driver上使用

driver的对象类,也是继承自WebElement

更多请阅读相关文档或源代码

from selenium import webdriver


driver_path = r'D:\PycharmProjects\chromedriver\chromedriver.exe'
# 定义chromedriver.exe所在的绝对路径

driver = webdriver.Chrome(executable_path=driver_path)
# 使用webdriver.Chrome初始化一个对象(driver),指定上面创建路径。


driver.get('https://www.baidu.com')                # 模拟打开页面

submitBth = driver.find_element_by_id('su')         # 获取到百度一下按钮
print(type(submitBth))        # 类型:
# 通过这个类型可以进行一系列的操作(获取到标签、属性及属性所对应的值,字符串等等...)

print(submitBth.get_attribute('value'))   # 获取到value属性所对应的值:'百度一下'四个字

driver.save_screenshot(r'C:\Users\Administrator\Desktop\baidu.png')    # 截图保存整个网页

相关文档:

selenium异常处理:
https://selenium-python.readthedocs.io/api.html#module-selenium.common.exceptions

selenium中文翻译文档:https://selenium-python-zh.readthedocs.io/en/latest

ChromeDriver下载镜像:
https://npm.taobao.org/mirrors/chromedriver
http://chromedriver.storage.googleapis.com/index.html


以上内容是看网易云的21天学会分布式爬虫时整理的笔记和自己平时积累的一些小东西


拓展:

https://www.cnblogs.com/pyspark/p/8195896.html

你可能感兴趣的:(网络爬虫,python,java,selenium,html,js)