基于Selenium的Ajax异步加载页面的数据采集技术
2024-04-06卢守东卢明俊
卢守东 卢明俊
关键词:数据采集;异步加载;Ajax;Selenium;Python
中图分类号:TP311 文献标识码:A
文章编号:1009-3044(2024)03-0001-03
0 引言
大数据与人工智能等新一代信息技术的快速发展与广泛应用,催生了对各类大量数据的强烈需求。而Web应用的持续发展与普遍应用,促使互联网逐渐成为各类大量数据的发源地与汇集地。因此,只要编写出相应的网络爬虫程序,即可从互联网中的有关Web站点(即网站)自动采集到所需要的数据[1]。
要从网页中采集数据,关键是要成功获取包含有相应数据的网页源代码。然而,为了改善应用的性能,并提高用户的体验,目前许多Web 站点均采用Ajax技术以实现页面数据的异步加载,其本质为Ja?vaScript动态渲染,即通过执行JavaScript脚本将异步请求返回的数据动态添加或更新到页面中。对于此类Ajax页面,若采用传统的常规方法,只能获取原始页面源代码,即数据异步加载之前的页面的源代码。在原始页面源代码中,并无加载完成后才呈现到页面中的数据。
那么,如何才能顺利地采集到Ajax页面中的异步加载数据呢?在此,将以Windows 7+ Python 3.8.18+ Se?lenium 4.13.0为开发环境,介绍一种基于Selenium的适用于Ajax异步加载页面的数据采集技术,供大家参考。
1 Selenium 简介
Selenium是目前常用的一种开源、免费的Web应用程序自动化测试工具[2],具有多平台(Linux、Win?dows、Macintosh等)、多浏览器(IE、Chrome、Firefox、Sa?fari、Opera、Edge等)与多语言(Python、Java、JavaScript、C++、C#、Ruby等)支持的特点。基于Selenium,可控制浏览器的运行,模拟用户在浏览器中的实际操作,并获取数据加载完毕后的页面源代码,因此可用于各种页面(包括Ajax页面)数据的采集。
Selenium 在Python 中是作为一个第三方库提供的,其安装方法很简单,只须执行“pip install sele?nium”命令即可。为提高安装的速度,可在执行该命令时通过选项“-i”指定相应的国内镜像(如https://pypi.tuna.tsinghua.edu.cn/simple) [3]。
Selenium必须与浏览器结合方可使用,因此还要安装相应的浏览器驱动。以Chrome为例,其驱动的下载地址为https://chromedriver.storage.googleapis.com/index. html。获取与Chrome 版本相对应的驱动chromedriver.exe后,只须将其置于某一目录,然后再将该目录添加到环境变量Path中即可[4]。在此,建议将其放至Python的安装目录。
2 主要技术
2.1 浏览器的启动与退出
为启动Chrome浏览器,只须调用selenium库web?driver模块的Chrome()函数即可。该函数的返回值为一个浏览器对象。反之,通过调用浏览器对象的quit() 方法,即可退出浏览器。
2.2 网页的访问
以指定的网址作为参數调用浏览器对象的get() 方法,即可实现对相应网页的访问。反之,直接调用浏览器对象的close()方法,即可关闭当前页面。成功打开网页后,可通过浏览器对象的page_source属性获取当前页面的源代码。必要时,也可调用浏览器对象的maximize_window()或minimize_window()方法,以实现浏览器窗口的最大化或最小化。例如:
from selenium import webdriver
browser=webdriver.Chrome()
browser.maximize_window()
browser.get('https://fanyi.baidu.com')
print(browser.page_source)
browser.quit()
2.3 页面元素的查找
页面中的元素多种多样。只有准确获取到页面中的有关元素,才能顺利采集到其中所包含的数据。可喜的是,Selenium提供了一系列元素查找方法,可灵活实现页面元素的获取或定位。
例如,调用浏览器对象的find_element()方法,可根据元素的ID、Name、Xpath路径、标签名、链接文本、部分链接文本、CSS类名、CSS选择器在当前页面中查找元素,并返回相应的一个元素对象。若将该方法名中的element改为复数形式elements,则可查找到符合指定条件的所有元素,并返回相应的一个元素对象列表。需要注意的是,在调用这两个方法前,应先从se?lenium.webdriver.common.by模块导入By类,以便利用该类的相应属性指定查找方式。By类的元素查找方式属性共有8 个,分别为ID、NAME、XPATH、TAG_NAME、LINK_TEXT、PARTIAL_LINK_TEXT、CLASS_NAME与CSS_SELECTOR。
若所使用的Selenium版本较低,find_element()方法的功能还可分别通过相应的find_element_by_xxx() 方法实现(其中的xxx表示id、name、xpath、tag_name、link_text、partial_link_text、class_name或css_selector) 。若将此类方法名中的element改为elements,则可实现find_elements()方法的相应功能。
基于已获取到的元素对象,必要时可进一步调用相应的元素查找方法以查找与其相关的其他元素,包括父元素、子元素、兄弟元素等。
2.4 元素的信息获取
通常,获取页面的源代码后,还须借助某个解析库来提取其中的数据。但对于Selenium来说,无须额外解析库的支持,也可顺利获取页面中有关元素的信息。
在Selenium中,元素对象的类型为WebElement。对于元素对象,只须访问其text属性,即可获取相应页面元素的文本信息[5]。必要时,还可访问其id、tag_name、location与size等属性,以便获取相应元素的ID、标签名、位置与大小等信息。此外,根据属性名调用元素对象的get_attribute()方法,即可获取相应页面元素指定属性的值。
2.5 元素的交互操作
在浏览网页的过程中,通常还需要执行相应的操作,如输入内容、单击链接、提交表单等。为模拟诸如此类的交互操作,Selenium针对元素对象也提供了相应的交互方法,包括send_keys()、click()、submit()等。其中,send_keys()方法可模拟向元素输入指定的内容,click()可模拟单击元素,submit()方法可模拟提交表单。
例如,在百度主页的左上角有一个“新闻”链接,相应元素的Xpath路径为“//*[@id="s-top-left"]/a[1]”。为获取该链接的文本与目标地址,然后再单击之,关键代码如下:
browser.get('https://www.baidu.com')
news=browser. find_element(By. XPATH, '//*[@id="s-top-left"]/a[1]')
print(news.text)
print(news.get_attribute('href'))
news.click()
2.6 页面的自动滚动
对于Ajax页面来说,随着页面的滚动或下拉,才会有更多的数据动态加载并显示出来。因此,如何实现页面的自动滚动功能也是颇为关键的。
通常,页面的自动滚动可通过执行相应的JavaS?cript脚本实现。为执行JavaScript脚本,在Selenium中只须调用浏览器对象的execute_script()方法即可[5]。例如,为将页面右侧滚动条的位置设定为0,从而将页面滚动至最上方,代码如下:
browser. execute_script("document. documentEle?ment.scrollTop=0") 在此,若增大scrollTop属性的值,即可将页面向下滚动至相应位置。
在Selenium中,还可以通过其他方式实现页面的自动滚动。其中,最简单易行的办法就是模拟向页面发送PageUp、PageDown、Home、End等按键。为此,应先获取页面的body元素,然后再调用其send_keys()方法。此外,为指定相应的按键,须从selenium.web?driver.common.keys模块导入Keys类,该类的有关属性代表的就是相应的按键。例如,为将页面滚动至底部,关键代码如下:
browser. find_element(By. TAG_NAME, 'body'). send_keys(Keys.END)
2.7 页面的互相切换
有时浏览器会同时打开多个页面,因此页面之间的互相切换是不可避免的。在Selenium中,通过调用浏览器对象switch_to属性的window ()方法,即可切换至指定的页面窗口。为指定页面窗口,可利用浏览器对象的window_handles属性,该属性将返回当前会话中所打开的所有页面窗口所构成的一个列表[5]。例如:
from selenium import webdriver
browser=webdriver.Chrome()
browser.get('https://www.baidu.com')
browser.execute_script('window.open()')
browser.switch_to.window(browser.window_handles[1])
browser.get('http://www.hao123.com')
browser.switch_to.window(browser.window_handles[0])
browser.get('https://fanyi.baidu.com')
browser.quit()
3 Ajax 頁面数据采集实例——今日头条财经类新闻标题及其链接地址的采集
今日头条是一个典型的Ajax 网站,其网址为https://www.toutiao.com。在浏览器中打开今日头条主页(如图1所示),并借助开发者工具进行分析,可知“财经”链接所对应的元素为:
其Xpath路径为:
//*[@id= "root"]/div/div[5]/div[1]/div/div/div/div[1]/div/ul/li[5]/div/div
在今日头条主页中单击“财经”链接,即可显示相应的财经类新闻。若下拉页面,将会有更多的新闻动态加载并显示出来。经分析,可知各条新闻的有关信息均置于相应的div元素中,其父元素亦为一个div元素,Xpath 路径为“//*[@id="root"]/div/div[5]/div[1]/div/div/div/div[2]”。而在各条新闻所对应的div元素中,新闻标题链接所对应的a元素的Xpath路径为“./div/div[@class="feed-card-article-l"]/a”。
根据以上分析结果,即可编程实现今日头条财经类新闻标题及其链接地址的采集,代码如下:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys import time
browser=webdriver.Chrome()
browser.maximize_window()
browser.get("https://www.toutiao.com")
browser.implicitly_wait(10)
browser.find_element(By.XPATH,\
'//*[@id= "root"]/div/div[5]/div[1]/div/div/div/div[1]/div/ul/li[5]/div/div').click()
for i in range(5):
browser. find_element(By. TAG_NAME, 'body').send_keys(Keys.END)
time.sleep(5)
div_element0=browser.find_element(By.XPATH,\
'//*[@id="root"]/div/div[5]/div[1]/div/div/div/div[2]')
div_elements=div_element0. find_elements(By.XPATH,'./div')
for div_element in div_elements:
a_element=div_element.find_element(By.XPATH,\
'./div/div[@class="feed-card-article-l"]/a')
title=a_element.text
url=a_element.get_attribute('href')
with open('今日頭条财经新闻.txt','a',encoding='utf8') as f:
f.write(title+'|||'+url+'\n')
browser.quit()
运行该程序,所采集到的财经类新闻的标题与链接地址将以“|||”为分隔添加到文本文件“今日头条财经新闻.txt”中,且每条新闻独占一行,如图2所示。
4 结束语
对于Ajax页面的数据采集来说,如何获取与异步加载完成后呈现在浏览器中的页面相一致的包含有具体数据的源代码(即当前页面源代码)是至关重要的。由于Selenium支持JavaScript动态渲染,能够成功获取浏览器当前所呈现的页面的源代码,同时提供相应的HTML解析功能,因此基于Selenium的Ajax页面数据采集方案是切实可行、颇为通用的,可以实现“所见即可爬”。
【通联编辑:谢媛媛】