美文网首页视觉艺术
Python爬虫-进阶篇之动态网页数据抓取

Python爬虫-进阶篇之动态网页数据抓取

作者: 复苏的兵马俑 | 来源:发表于2020-04-16 17:07 被阅读0次

1、AJAX描述

   AJAX(Asynchronouse JavaScript And XML)异步JavaScript和XML,在后台与服务器进行少量数据交换,Ajax可以使网页实现异步更新,这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。传统的网页(不使用Ajax)如果需要更新内容,就必须重载整个网页页面,因为传统的在传输数据格式方面,使用的是XML语法,因此叫做AJAX,其实现在数据交互基本上都是使用JSON。使用Ajax加载的数据,即使使用了JS,将数据渲染到了浏览器中,在“右键->查看网页源代码”还是不能看到通过Ajax加载的数据,只能看到使用这个url加载的html代码。

2、获取Ajax数据的方式

   1)直接分析Ajax调用的接口,然后通过代码请求这个接口。
   2)使用Selenium + chromedriver模拟浏览器行为获取数据。

方式 优点 缺点
分析接口 直接可以请求到数据,
不需要做一些解析工作。
代码量少,性能高
分析接口比较复杂,
特别是一些通过js混淆的接口,
要有一定的js功底,
容易被发现是爬虫。
Selenium 直接模拟浏览器的行为,
浏览器能请求到的,
使用Selenium也能请求到,
爬虫更稳定
代码量多,性能低

3、Selenium + chromedriver获取动态数据

   Selenium相当于是一个机器人,可以模拟人类在浏览器上的一些行为,自动处理浏览器上的一些行为,比如点击、填充数据、删除cookie等。
   chromedriver是一个驱动Chrome浏览器的驱动程序,使用他才可以驱动浏览器,当然针对不同的浏览器有不同的driver,以下列出了不同浏览器机器对应的driver:
   a)Chrome:https://sites.google.com/a/chromium.org/chromedriver/downloads
   b)Firefox:https://github.com/mozilla/geckodriver/releases
   c)Edge:https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/
   d)Safari:https://webkit.org/blog/6900/webdriver-support-in-safari-10/

1)安装Selenium和chromedriver

   a)安装Selenium:Selenium有很多语言的版本,有Java、Ruby、Python等。
   b)安装chromedriver:下载完成后,放到不需要权限的纯英文目录下就可以了。

2)快速入门

   现在以一个简单的获取百度首页的例子来快速入门Selenium和chromedriver:

from selenium import webdriver

# chromedriver的绝对路径
driver_path = r"D:\chromedriver\chromedriver.exe"

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

# 请求网页
driver.get('https://www.baidu.com')

# 通过page_source获取网页源代码
print(driver.page_source)

   运行结果:自动打开Chrome浏览器如下图所示,并且打印出网页源代码。


自动打开的Chrome浏览器
3)Selenium常用操作

   更多教程请参考:http://selenium-python.readthedocs.io/installation.html#introduction
   A)关闭页面
   A.1)driver.close():关闭当前页面
   A.2)driver.quit():退出整个浏览器

from selenium import webdriver
import time

driver_path = r"D:\chromedriver\chromedriver.exe"
driver = webdriver.Chrome(executable_path=driver_path)
driver.get('https://www.baidu.com')

time.sleep(5)
driver.close()
time.sleep(5)
driver.quit()

   B)定位元素
   注:find_element是获取第一个满足条件的元素,find_elements是获取所有满足条件的元素。
   B.1)find_element_by_id:根据id来查找元素,等价于:

submitTag = driver.find_element_by_id('su')
submitTag1 = driver.find_element(By.ID, 'su')

   示例代码(1)如下:

from selenium import webdriver

driver_path = r"D:\chromedriver\chromedriver.exe"
driver = webdriver.Chrome(executable_path=driver_path)
driver.get('https://www.baidu.com')

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

   示例代码(2)如下:

from selenium import webdriver
from selenium.webdriver.common.by import By

driver_path = r"D:\chromedriver\chromedriver.exe"
driver = webdriver.Chrome(executable_path=driver_path)
driver.get('https://www.baidu.com')

inputTag = driver.find_element(By.ID, 'kw')
inputTag.send_keys('python')

   B.2)find_element_by_class_name:根据类名查找元素,等价于:

submitTag = driver.find_element_by_class_name('su')
submitTag1 = driver.find_element(By.CLASS_NAME, 'su)

   示例代码如下:

from selenium import webdriver

driver_path = r"D:\chromedriver\chromedriver.exe"
driver = webdriver.Chrome(executable_path=driver_path)
driver.get('https://www.baidu.com')

inputTag = driver.find_element_by_class_name('s_ipt')
inputTag.send_keys('python')

   B.3)find_element_by_name:根据name属性的值来查找元素,等价于:

submitTag = driver.find_element_by_name('email')
submitTag1 = driver.find_element(By.NAME, 'email')

   示例代码如下:

from selenium import webdriver

driver_path = r"D:\chromedriver\chromedriver.exe"
driver = webdriver.Chrome(executable_path=driver_path)
driver.get('https://www.baidu.com')

inputTag = driver.find_element_by_name('wd')
inputTag.send_keys('python')

   B.4)find_element_by_tag_name:根据标签名来查找元素,等价于:

submitTag = driver.find_element_by_tag_name('div')
submitTag1 = driver.find_element(By.TAG_NAME,'div')

   B.5)find_element_by_xpath:根据xpath语法来获取元素,等价于:

submitTag = driver.find_element_by_xpath('//div')
submitTag1 = driver.find_element(By.XPATH, '//div')

   示例代码如下:

from selenium import webdriver

driver_path = r"D:\chromedriver\chromedriver.exe"
driver = webdriver.Chrome(executable_path=driver_path)
driver.get('https://www.baidu.com')

inputTag = driver.find_element_by_xpath("//input[@id='kw']")
inputTag.send_keys('python')

   B.6)find_element_by_css_selector:根据css选择器选择元素,等价于:

submitTag = driver.find_element_by_css_selector('//div')
submitTag1 = driver.find_element(By.CSS_SELECTOR, '//div')

   示例代码(1)如下:

from selenium import webdriver

driver_path = r"D:\chromedriver\chromedriver.exe"
driver = webdriver.Chrome(executable_path=driver_path)
driver.get('https://www.baidu.com')

inputTag = driver.find_element_by_css_selector(".quickdelete-wrap > input")
inputTag.send_keys('python')

   示例代码(2)如下:

from selenium import webdriver

driver_path = r"D:\chromedriver\chromedriver.exe"
driver = webdriver.Chrome(executable_path=driver_path)
driver.get('https://www.baidu.com')

inputTag = driver.find_elements_by_css_selector(".quickdelete-wrap > input")[0]
inputTag.send_keys('python')

   C)操作表单元素
   C.1)常用表单元素
     input:type = 'text/password/email/number'
     button、input:type = 'submit'
     checkbox:type = 'checkbox'
     select:下拉列表
   C.2)操作输入框:分为两步,第一步:找到这个元素,第二步:使用send_keys(value)将数据填充进去,示例代码如下:

from selenium import webdriver

driver_path = r"D:\chromedriver\chromedriver.exe"
driver = webdriver.Chrome(executable_path=driver_path)
driver.get('https://www.baidu.com')

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

   使用clear方法可以清楚输入框中的内容,示例代码如下:

from selenium import webdriver
import time

driver_path = r"D:\chromedriver\chromedriver.exe"
driver = webdriver.Chrome(executable_path=driver_path)
driver.get('https://www.baidu.com')

inputTag = driver.find_element_by_id('kw')
inputTag.send_keys('python')
time.sleep(3)
inputTag.clear()

   C.3)操作checkbox:因为要选中checkbox标签,在网页中是通过鼠标点击的,因此想要选中checkbox标签,先选中这个标签,然后执行click事件,示例代码如下:

from selenium import webdriver

driver_path = r"D:\chromedriver\chromedriver.exe"
driver = webdriver.Chrome(executable_path=driver_path)
driver.get('https://www.douban.com')

elementi = driver.find_element_by_tag_name('iframe')    # 定位到iframe
driver.switch_to.frame(elementi)    # 切入iframe

rememberBtn = driver.find_element_by_name('remember')
rememberBtn.click()

   C.4)操作select:select元素不能直接点击,因为点击后还需要选中元素,这时候selenium就专门为select标签提供了一个类selenium.webdriver.support.ui.Select,将获取到的元素当成参数传到这个类中,创建这个对象,以后就可以使用这个对象进行选择了,示例代码如下:

from selenium import webdriver
from selenium.webdriver.support.ui import Select

driver_path = r"D:\chromedriver\chromedriver.exe"
driver = webdriver.Chrome(executable_path=driver_path)
driver.get('http://xianyang06360.11467.com/')

selectBtn = Select(driver.find_element_by_name('nsid')) # 选中这个标签,然后使用Select创建对象
# selectBtn.select_by_index(1)  # 根据索引选择
# selectBtn.select_by_value('2')    # 根据值选择
selectBtn.select_by_visible_text('产品')    # 根据可视的文本选择
# selectBtn.deselect_all()    # 取消选中所有选项

   C.5)操作按钮:操作按钮有很多种方式,比如单击、右击、双击等,这里只讲最常用的单击,直接调用click函数就可以,示例代码如下:

from selenium import webdriver

driver_path = r"D:\chromedriver\chromedriver.exe"
driver = webdriver.Chrome(executable_path=driver_path)
driver.get('https://www.baidu.com')

inputTag = driver.find_element_by_id('kw')
inputTag.send_keys('python')
clickBtn = driver.find_element_by_id('su')
clickBtn.click()

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

from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains

driver_path = r"D:\chromedriver\chromedriver.exe"
driver = webdriver.Chrome(executable_path=driver_path)
driver.get('https://www.baidu.com')

inputTag = driver.find_element_by_id('kw')
submitTag = driver.find_element_by_id('su')

actions = ActionChains(driver)
actions.move_to_element(inputTag)
actions.send_keys_to_element(inputTag, 'python')
actions.move_to_element(submitTag)
actions.click(submitTag)
actions.perform()

   还有更多的鼠标相关的操作:
   a)click_and_hold(element):点击但不松开鼠标
   b)context_click(element):右键点击
   c)double_click(element):双击
   更多方法请参考:http://selenium-python.readthedocs.io/api.html

   E)操作Cookie
   E.1)获取所有的cookie

from selenium import webdriver

driver_path = r"D:\chromedriver\chromedriver.exe"
driver = webdriver.Chrome(executable_path=driver_path)
driver.get('https://www.baidu.com')

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

   运行结果:

{'domain': '.baidu.com', 'httpOnly': False, 'name': 'H_PS_PSSID', 'path': '/', 'secure': False, 'value': '30973_1449_31118_21086_31187_30824_26350_31164_31195'}
{'domain': '.baidu.com', 'expiry': 1618558602.689842, 'httpOnly': False, 'name': 'BAIDUID', 'path': '/', 'secure': False, 'value': 'A5F0BE95DD1F26F0AA75AB29BD2F7285:FG=1'}
{'domain': 'www.baidu.com', 'httpOnly': False, 'name': 'BD_HOME', 'path': '/', 'secure': False, 'value': '1'}
{'domain': '.baidu.com', 'expiry': 3734506249.689763, 'httpOnly': False, 'name': 'PSTM', 'path': '/', 'secure': False, 'value': '1587022603'}
{'domain': 'www.baidu.com', 'expiry': 1587886603, 'httpOnly': False, 'name': 'BD_UPN', 'path': '/', 'secure': False, 'value': '12314753'}
{'domain': '.baidu.com', 'expiry': 3734506249.689632, 'httpOnly': False, 'name': 'BIDUPSID', 'path': '/', 'secure': False, 'value': 'A5F0BE95DD1F26F0CF89F6000DA5C1D6'}

   E.2)根据cookie的key获取value

from selenium import webdriver

driver_path = r"D:\chromedriver\chromedriver.exe"
driver = webdriver.Chrome(executable_path=driver_path)
driver.get('https://www.baidu.com')

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

print('-' * 100)

print(driver.get_cookie('PSTM'))

   运行结果:

{'domain': '.baidu.com', 'httpOnly': False, 'name': 'H_PS_PSSID', 'path': '/', 'secure': False, 'value': '30969_1447_31169_21090_31217_30824_31163_22158'}
{'domain': '.baidu.com', 'expiry': 1618558826.5253, 'httpOnly': False, 'name': 'BAIDUID', 'path': '/', 'secure': False, 'value': 'CCD56BC52CA0797650FF41F1DE8C7A07:FG=1'}
{'domain': 'www.baidu.com', 'httpOnly': False, 'name': 'BD_HOME', 'path': '/', 'secure': False, 'value': '1'}
{'domain': '.baidu.com', 'expiry': 3734506473.52528, 'httpOnly': False, 'name': 'PSTM', 'path': '/', 'secure': False, 'value': '1587022827'}
{'domain': 'www.baidu.com', 'expiry': 1587886827, 'httpOnly': False, 'name': 'BD_UPN', 'path': '/', 'secure': False, 'value': '12314753'}
{'domain': '.baidu.com', 'expiry': 3734506473.525246, 'httpOnly': False, 'name': 'BIDUPSID', 'path': '/', 'secure': False, 'value': 'CCD56BC52CA079760E777777C96D8FFC'}
====================================================================================================
{'domain': '.baidu.com', 'expiry': 3734506473.52528, 'httpOnly': False, 'name': 'PSTM', 'path': '/', 'secure': False, 'value': '1587022827'}

   E.3)删除某个cookie

from selenium import webdriver

driver_path = r"D:\chromedriver\chromedriver.exe"
driver = webdriver.Chrome(executable_path=driver_path)
driver.get('https://www.baidu.com')

print(driver.get_cookie('PSTM'))

driver.delete_cookie('PSTM')
print('-' * 100)

print(driver.get_cookie('PSTM'))

   运行结果:

{'domain': '.baidu.com', 'expiry': 3734506620.415989, 'httpOnly': False, 'name': 'PSTM', 'path': '/', 'secure': False, 'value': '1587022974'}
----------------------------------------------------------------------------------------------------
None

   E.4)删除所有的cookie

from selenium import webdriver

driver_path = r"D:\chromedriver\chromedriver.exe"
driver = webdriver.Chrome(executable_path=driver_path)
driver.get('https://www.baidu.com')

for cookie in driver.get_cookies():
    print(cookie)
print('-' * 100)

driver.delete_all_cookies()
print('-' * 100)

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

   运行结果:

{'domain': '.baidu.com', 'httpOnly': False, 'name': 'H_PS_PSSID', 'path': '/', 'secure': False, 'value': '30971_1456_21108_31229_30823_31164'}
{'domain': '.baidu.com', 'expiry': 1618559078.01659, 'httpOnly': False, 'name': 'BAIDUID', 'path': '/', 'secure': False, 'value': '2816B00D030A93B74E6FC721AC4A617C:FG=1'}
{'domain': 'www.baidu.com', 'httpOnly': False, 'name': 'BD_HOME', 'path': '/', 'secure': False, 'value': '1'}
{'domain': '.baidu.com', 'expiry': 3734506725.016538, 'httpOnly': False, 'name': 'PSTM', 'path': '/', 'secure': False, 'value': '1587023079'}
{'domain': 'www.baidu.com', 'expiry': 1587887078, 'httpOnly': False, 'name': 'BD_UPN', 'path': '/', 'secure': False, 'value': '12314753'}
{'domain': '.baidu.com', 'expiry': 3734506725.016481, 'httpOnly': False, 'name': 'BIDUPSID', 'path': '/', 'secure': False, 'value': '2816B00D030A93B7890B7B46FAE224A6'}
----------------------------------------------------------------------------------------------------

   F)页面等待
   现在的网页越来越多采用了Ajax技术,这样程序便不能确定何时某个元素完全加载出来了,如果实际页面等待时间过长导致某个dom元素还没有出来,但是你的代码直接使用了这个WebElement,那么就会抛出NullPointer的异常。为了解决这个问题,Selenium提供了两种等待方式:一种是隐式等待,一种是显式等待。
   F1)隐式等待:调用deriver.implicitly_wait,在获取不可用的元素之前,会先等待10秒钟的时间,示例代码如下:

from selenium import webdriver

driver_path = r"D:\chromedriver\chromedriver.exe"
driver = webdriver.Chrome(executable_path=driver_path)
driver.get('https://www.douban.com')

driver.implicitly_wait(10)

driver.find_element_by_id('lkjlkjl')

   运行结果:

selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"css selector","selector":"[id="lkjlkjl"]"}
  (Session info: chrome=81.0.4044.9)

   F2)显式等待:显式等待是表明某个条件成立后才执行获取元素的操作,也可以在等待的时候指定一个最大的时间,如果超过这个时间就抛出一个异常,显式等待应该使用selenium.webdriver.support.excepted_conditions期望的条件和selenium.webdriver.support.ui.WebDriverWait来配合完成。
   示例代码(1)如下:

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

driver_path = r"D:\chromedriver\chromedriver.exe"
driver = webdriver.Chrome(executable_path=driver_path)
driver.get('https://www.douban.com')

try:
    element = WebDriverWait(driver, 10).until(
        EC.presence_of_element_located((By.ID, 'lkjlkj'))
    )
finally:
    driver.quit()

   运行结果:

selenium.common.exceptions.TimeoutException: Message: 

   示例代码(2)如下:

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

driver_path = r"D:\chromedriver\chromedriver.exe"
driver = webdriver.Chrome(executable_path=driver_path)
driver.get('https://www.douban.com')

elementi = driver.find_element_by_tag_name('iframe')    # 定位到iframe
driver.switch_to.frame(elementi)    # 切入iframe

try:
    element = WebDriverWait(driver, 10).until(
        EC.presence_of_element_located((By.NAME, 'phone'))
    )
finally:
    driver.quit()
print(element)

   运行结果:

<selenium.webdriver.remote.webelement.WebElement (session="72ffcfd184c6457786fb6b428aa09d88", element="43714d55-52a5-4772-8566-485fe50acda8")>

   F.3)其他的一些等待条件:
   a)presence_of_element_located:某个元素已经加载完毕了。
   b)presence_of_all_element_located:网页中所有满足条件的元素都加载完毕了。
   c)element_to_be_cliable:某个元素是可以点击了。
   更多条件请参考:http://selenium-python.readthedocs.io/waits.html

   G)切换页面
   有时候窗口中有很多子tab页面,这时候肯定是需要进行切换的,selenium提供了switch_to_window来进行切换,具体切换到哪个页面,可以从driver.window_handles中找到,示例代码如下:

from selenium import webdriver

driver_path = r"D:\chromedriver\chromedriver.exe"
driver = webdriver.Chrome(executable_path=driver_path)
driver.get('https://www.baidu.com')

# driver.get('https://www.douban.com')
driver.execute_script("window.open('https://www.douban.com')")  # 打开一个新的页面
print(driver.window_handles)
print('切换之前的网页是{}'.format(driver.current_url))
driver.switch_to.window(driver.window_handles[1])   # 切换到新的页面中
print('切换之后的网页是{}'.format(driver.current_url))

   运行结果:

['CDwindow-7AC07DE61FD21770433360D8416CA254', 'CDwindow-9904C78191603F1D626361548A8E9930']
切换之前的网页是https://www.baidu.com/
切换之后的网页是https://www.douban.com/

   虽然在窗口中切换到了新的页面,但是driver中还没有切换,如果想要在代码中切换到新的页面,并且做一些爬虫,那么应该使用driver.swtich_to_window来切换到指定的窗口。从driver.window_handlers中取出具体第几个窗口。driver.window_handlers是一个列表,里面装的都是窗口句柄,会按照打开页面的顺序来存储窗口的句柄。

   H)设置代理IP
   有时候频繁爬取一些网页,服务器发现你是爬虫后悔封掉你的IP地址,这时候我们可以更改代理IP。更改代理IP,不同的浏览器有不同的方式,这里以Chrome浏览器为例,示例代码如下:

from selenium import webdriver
driver_path = r"D:\chromedriver\chromedriver.exe"
options = webdriver.ChromeOptions()
options.add_argument('--proxy-server=http://171.35.167.190:9999')

driver = webdriver.Chrome(executable_path=driver_path, options=options)
driver.get('http://httpbin.org/ip')

   I)WebElement元素
   from selenium.webdriver.remote.webelement import WebElement类是每个获取出来的元素的所属类.
   一些常用的属性:
   a)get_attribute:这个标签的某个属性的值。
   b)screenshot:获取当前页面的截图,这个方法只能在driver上使用,driver的对象类,也是继承自WebElement
   更多请阅读相关源代码。
   示例代码如下:

from selenium import webdriver

driver_path = r"D:\chromedriver\chromedriver.exe"
driver = webdriver.Chrome(executable_path=driver_path)
driver.get('https://www.baidu.com')

submitBtn = driver.find_element_by_id('su')
print(type(submitBtn))
print(submitBtn.get_attribute('value'))
driver.save_screenshot('baidu.png')

   运行结果:

<class 'selenium.webdriver.remote.webelement.WebElement'>
百度一下

4、Selenium、chromedriver和lxml结合使用

   A)如果只是想要解析网页中的数据,那么推荐将网页源代码扔给lxml来解析,因为lxml底层使用的是C语言,所以解析效率会更高。
   B)如果是想要对元素进行一些操作,比如给一个文本框输入值,或者是点击某个按钮,那么就必须使用selenium给我们提供的查找元素的方法。
   示例代码如下:

from selenium import webdriver
from lxml import etree

driver_path = r"D:\chromedriver\chromedriver.exe"
driver = webdriver.Chrome(executable_path=driver_path)
driver.get('https://www.baidu.com')

html = etree.HTML(driver.page_source)
title = html.xpath('//div[@id="lg"]//map//@title')
print(title)

相关文章

网友评论

    本文标题:Python爬虫-进阶篇之动态网页数据抓取

    本文链接:https://www.haomeiwen.com/subject/ymthvhtx.html