如何使用Selenium抓取动态网页数据:AJAX基础与实战技巧

1.1 要介绍的是 AJAX 是什么。

如何使用Selenium抓取动态网页数据:AJAX基础与实战技巧

AJAX(异步 JavaScript 和 XML)能够在后台与服务器进行少量数据的交换,凭借此特性,Ajax 可以让网页实现异步更新,也就是说,在不重新加载整个网页的情况下,能够对网页的某一部分进行局部的更新。传统网页在不使用 Ajax 时,若要更新内容,就必须重新加载整个网页页面。

传统网页在传输数据格式上使用 XML 语法,所以被称作 AJAX。如今数据交互大多使用 JSON。使用 AJAX 加载的数据,即便通过 JS 渲染到了浏览器中,在右键点击并选择“查看网页源代码”时,也无法看到通过 ajax 加载的数据,只能看到使用该 url 加载的 html 代码。

1.2 获取ajax数据的方式

直接对 ajax 调用的接口进行分析。接着通过代码来请求这个接口。

使用 Selenium 以及 chromedriver 来模拟浏览器的行为,从而获取数据。

方式

优点

缺点

分析接口

可以直接请求到数据,无需进行解析工作,代码量少且性能高。

分析接口较为复杂,尤其对于一些经由 js 混淆的接口而言,需要具备一定的 js 基础功底。这样的接口容易被察觉是爬虫。

selenium

模拟浏览器的行为。浏览器能够请求到的内容,selenium 也能够请求到。并且爬虫更加稳定。

代码量多。性能低。

Selenium 结合 chromedriver 来获取动态数据。

Selenium 相当于一个机器人。它能够模拟人类在浏览器上的某些行为,能够自动处理浏览器上的一些行为,例如进行点击操作,填充数据,删除 cookie 等。chromedriver 是一个用于驱动 Chrome 浏览器的驱动程序,只有使用它才可以驱动浏览器。当然,针对不同的浏览器,有不同的驱动程序。以下列出了不同浏览器及其对应的driver:

Chrome、Firefox、Edge、Safari。1.4 需安装 Selenium 和 chromedriver。Selenium 有多种语言版本,包括 java、ruby、python 等。我们只需下载 python 版本即可。安装 chromedriver 可通过 pip install selenium 来实现。在下载完成后,将其放置于无需权限的纯英文目录中即可。这是快速入门的 1.5 部分内容。

现在来讲下以一个简单的获取百度首页为例,Selenium 和 chromedriver 如何快速入门:

使用 selenium 库中的 webdriver 类。
# chromedriver的绝对路径
driver_path 为 r'D:\ProgramApp\chromedriver\chromedriver.exe'
初始化一个 driver,同时明确指定 chromedriver 的路径。
driver 等于使用 Chrome 浏览器驱动并设置可执行路径为 driver_path 的 webdriver.Chrome 对象。
# 请求网页
driver 打开了 "https://www.baidu.com/" 这个网址。
# 通过page_source获取网页源代码
print(driver.page_source)

1.6 selenium常用操作

参考:Selenium的使用

1.7 爬取拉钩网职位信息

直接直接分析ajax调用的接口爬取

import requests
from lxml import etree
import time
import re
headers = {
"Accept" 的值为 "application/json, text/javascript, */*; q=0.01" 。
"Accept-Encoding" 包含 "gzip"、"deflate" 和 "br" 这几种编码方式。
"Accept-Language" 的值为 "zh-CN" 以及 "zh" 且其权重为 "0.9" 。
    "Connection":"keep-alive",
User-Agent 为 Mozilla/5.0,其运行的系统是 Windows NT 10.0,且是 64 位系统(Win64),同时也是 x64 架构。该浏览器的内核是 AppleWebKit/537.36,其渲染引擎类似于 Gecko,浏览器的版本是 Chrome/73.0.3683.86,其渲染引擎的名称是 Safari/537.36。
"Referer"的值为"https://www.lagou.com/jobs/list_python",其中包含"labelWords=&fromSearch=true&suginput="等内容。
Origin 为 "https://www.lagou.com"。
    "Host":"www.lagou.com",
"Content-Type"为"application/x-www-form-urlencoded",并且字符编码为"UTF-8"。
"_ga=GA1.2.1602115737.1553064534;user_trace_token=20190320144853-39b1375a-4adc-11e9-a253-525400f775ce;LGUID=20190320144853-39b13f88-4adc-11e9-a253-525400f775ce;WEBTJ-ID=20190408120043-169fb1afd63488-06179b118ca307-7a1437-2073600-169fb1afd648ed;_gid=GA1.2.1826141825.1554696044;sensorsdata2015jssdkcross={\"distinct_id\":\"169fb1bb2c41ea-04951e55adc96a-7a1437-2073600-169fb1bb2c58d0\",\"$device_id\":\"169fb1bb2c41ea-04951e55adc96a-7a1437-2073600-169fb1bb2c58d0\"};sajssdk_2015_cross_new_user=1;_putrc=4C5D2603888320CA;JSESSIONID=ABAAABAAADEAAFIB00F5DDE71D51610901CB9E0031812BA;login=true;unick=禤泽宁;showExpriedIndex=1;showExpriedCompanyHome=1;showExpriedMyPublish=1;hasDeliver=49;gate_login_token=7b04a40da89145a1fbc90a3d719616d28c8b0a303344ac37;index_location_city=成都;X_MIDDLE_TOKEN=1221e6b5040722dc86f5ceb557e11965;_gat=1;LGSID=20190408151935-a9976fbf-59ce-11e9-8cc8-5254005c3644;PRE_UTM=m_cf_cpc_baidu_pc;PRE_HOST=www.baidu.com;PRE_SITE=https://www.baidu.com/baidu.php?sc.Ks000001qLT2daZnZWIez3ktR_jhHue3tONZubxU9mivhxeuj-Fxrjg6NnVcKTp-GYJ_YRvrc9_yOJ4uV-IEpfnPazPz7ctjve1qlDokCDfHYo9PV0uDfTmN1OunNUcCRU-sJuR8RZz60PAXzfKybAdvuCxUedbt8aWtTjAdCCuO298TwT8zN1-T5EG3kgkOweg0DHGIbvP55IZbr6.DY_NR2Ar5Od663rj6tJQrGvKD7ZZKNfYYmcgpIQC8xxKfYt_U_DY2yP5Qjo4mTT5QX1BsT8rZoG4XL6mEukmryZZjzsLTJplePXO-8zNqrw5Q9tSMj_qTr1x9tqvZul3xg1sSxW9qx-9LdoDkY4QPSl81_4pqO24rM-8dQjPakb3dS5iC0.U1Yk0ZDqs2v4VnL30ZKGm1Yk0Zfqs2v4VnL30A-V5HcsP0KM5g
    "X-Anit-Forge-Code":"0" ,
    "X-Anit-Forge-Token":'',
X-Requested-With 这个值为 XMLHttpRequest
}
def get_detail_page_url():
    datas =[]
该 url 为 'https://www.lagou.com/jobs/positionAjax.json'
    form_data = {
        "first":"faise",
        "pn":1,
        "kd":"python"
    }
    params = {
        'city':'成都',
        'needAddtionalResult':'false'
    }
    for pn in range(1,14):
        form_data['pn'] = pn
使用 requests 库发送一个 POST 请求,请求的 URL 是指定的 url,请求头是 headers,请求参数是 params,请求体是 form_data,然后将响应存储在 response 变量中。
        result = response.json()
result 中的 content 部分的 positionResult 中的 result 被赋值给了 result_list
        for position in result_list:
position_id 等于 position 中的 positionId
detail_url 等于 'https://www.lagou.com/jobs/' 加上 position_id 再加上.html
使用 detail_url 来解析详情页面,得到的数据为 data 。
            datas.append(data)
            time.sleep(2)
    return datas
def parse_detail_page(url):
使用 requests 库发送一个 GET 请求,请求的 URL 为 url,请求头为 headers,将返回的响应赋值给 resonse
    text = resonse.text
使用 etree 的 HTMLParser 对 text 进行解析,然后通过 etree.fromstring 方法将其转换为 html 对象。
通过 html.xpath('//span[@class="name"]/text()') 获取到的第一个元素,将其去除两端的空白字符后赋值给 position_name
detail_list 是通过 html.xpath('//dd[@class="job_request"]//span')获取到的。
salary 等于 detail_list 中的第一个元素通过 xpath 获取到的文本内容,然后再去掉该文本内容的首尾空格
city 等于 detail_list 中的第一个元素通过 xpath 获取到的文本内容,然后再去除该文本内容两端的空白字符
city 被替换为通过正则表达式 r'[\s/]' 替换为空字符后的结果
work_years 等于 detail_list 中第二个元素通过 xpath 获取到的文本内容,然后再取其第一个元素并去除两端的空白字符。
work_years 等于将字符串中的空格和斜杠去除后的结果,即对 work_years 进行了去除空格和斜杠的操作,具体是通过 re.sub(r'[\s/]','',work_years) 实现的。
语言学习的教育方面等于详细列表中的第三个元素通过 xpath 获取到的文本内容,并且去掉该文本内容的首尾空格。
具体到“education = re.sub(r'[\s/]','',education)”这部分内容,可改写为:语言风格保持不变,要去除前面的序号。对于“education = re.sub(r'[\s/]','',education)”这一语句,需将其拆分成多个小分句,每个分句都要简洁明了。
通过以下方式获取 job_details:将从 html 中通过 xpath 选取到的 //div[@class="job-detail"]//p//text() 的内容进行拼接,拼接后的结果赋值给 job_details 。
    data = {
"position_name" 为 position_name 。
        "salay":salary,
        "city":city,
        "work_years":work_years,
        "education":education,
        "job_details":job_details
    }
    return data
def main():
    datas = get_detail_page_url()
    print(datas)
if __name__ == '__main__':
    main()

selenium结合lxml爬取

from selenium import webdriver
从 selenium 的 webdriver.support.ui 模块中导入 WebDriverWait 类。
Selenium 的 WebDriver 支持中包含了 expected_conditions 并将其记为 EC 。
在 selenium 的 webdriver 中,通过 common.by 来导入 By 这个类。
from lxml import etree
import re
import time
class Lagouspider(object):
driver_path 等于 r'E:\study\chromedriver\chromedriver.exe'
    def __init__(self):
self.driver 是通过使用 webdriver.Chrome 并指定 executable_path 为 Lagouspider.driver_path 来创建的。
self.url 等于 'https://www.lagou.com/jobs/list_python',其中 labelWords 为空,fromSearch 为 true,suginput 为空
        self.positions = []
    def run(self):
        while True:
            self.driver.get(self.url)
self.driver 的页面源代码即为 source 。
WebDriverWait,其 driver 为 self.driver,timeout 为 10,等待直到 EC.presence_of_element_located 这个条件满足,条件中的元素定位是 (By.XPATH,'//div[@class="pager_container"]/span[last()]')
            self.parse_list_page(source)
在页面中,通过以下 XPath 表达式来查找元素://div[@class="pager_container"]/span[last()],找到的元素即为 next_btn 。
如果“pager_next_disabled”在 next_btn 的 class 属性中:
                break
            else:
                next_btn.click()
            time.sleep(1)
通过特定的方式对 source 进行处理来完成对列表页面的解析工作。
        html = etree.HTML(source)
links 是通过 html.xpath('//a[@class="position_link"]/@href') 获取到的
        for link in links:
调用 self 的 request_detail_page 函数并传入 link 参数。
            time.sleep(1)
它定义了一个名为 request_detail_page 的方法,该方法接收一个名为 url 的参数。
self.driver 执行了一段脚本,该脚本的内容是打开一个新的窗口,窗口的地址是通过字符串格式化将 url 插入到 "%s" 中形成的,即 "%s" 被替换为实际的 url 地址,从而实现打开指定 url 的新窗口的功能。
self.driver 切换至 self.driver.window_handles[1] 所对应的窗口
使用 WebDriverWait 并设置超时时间为 10,等待直到找到位于 XPath '//span[@class="name"]' 的元素
self.driver 的页面源代码被赋值给了 source 。
调用 self.parse_detail_page 函数并传入 source 参数。
        #关闭当前详情页
        self.driver.close()
        #切换回职位列表页
self.driver 切换到 self.driver.window_handles[0]所对应的窗口。
在处理详情页面的过程中,涉及到对 source 的解析工作。
        html = etree.HTML(source)
通过 html.xpath('//span[@class="name"]/text()') 找到的第一个元素,将其文本内容去除两端的空白字符后赋值给 position_name
detail_list 等于 html 通过 xpath 选取到的 //dd[@class="job_request"]//span 这个部分
salary 等于 detail_list 中的第一个元素通过 xpath 获取到的文本,然后再去掉该文本两端的空格。
city 等于 detail_list 中索引为 1 的元素通过 xpath 方法获取到的文本内容,然后再取该文本内容的第一个字符并去除其两侧的空白字符
city 把包含空格或斜杠的部分替换为了空字符。
work_years 等于 detail_list 中索引为 2 的元素通过 xpath 获取的文本内容,然后再取该文本内容的第一个元素并去除两端的空白字符。
使用 re.sub 函数,将 work_years 中的空格和斜杠去掉,即把其中的空白字符和斜杠替换为空字符串。具体来说,就是通过正则表达式 r'[\s/]' 匹配到 work_years 中的空格和斜杠,然后用空字符串进行替换,从而得到去掉空格和斜杠后的 work_years。
语言学习的教育方面等于详细列表中第三个元素通过 xpath 获取到的文本内容,然后去除该文本内容两端的空白字符。
语言风格需保持原文风格,去掉最前面的序号,将长句拆分成多个小分句,分句越短越好。即对“education = re.sub(r'[\s/]', '', education)”这句代码进行改写,避免使用原文用过的词语,对句子结构和用词进行调整、充实,保留原意,禁止修改专有名词,禁止穿插英文单词。改写后的句子为:将 education 中的空格和斜杠去掉,通过 re.sub(r'[\s/]', '', education)来实现。
使用 html 的 xpath 方法获取 //dd[@class="job_bt"] 下的所有文本节点,然后将这些文本节点连接成一个字符串,最后去除字符串两端的空白字符,将结果赋值给变量 desc 。
        data = {
            "name": position_name,
            "salay": salary,
            "city": city,
            "work_years": work_years,
            "education": education,
            "desc": desc
        }
        print(data)
        print('+'*40)
        self.positions.append(data)
if __name__ == '__main__':
    spider = Lagouspider()
    spider.run()

希望本文能对你提供帮助。如果你对软件测试、接口测试、自动化测试、面试经验交流感兴趣,那么可以私聊我,或者关注公众号“特斯汀软件测试”。这里可以免费领取最新的软件测试大厂面试资料,以及 Python 自动化、接口、框架搭建的学习资料!还能让技术大牛为你解惑答疑,与同行一起交流。

你可能想看:
« 上一篇
下一篇 »

相关推荐

java中什么是冒泡,冒泡算法

2026年09月06日

27阅读

java中冒号,java双冒号

2026年09月06日

26阅读

java中的->,java中的是什么意思

2026年09月06日

24阅读

java中的super用法,Java中super的说明

2026年09月06日

28阅读

java中反射的应用,java中反射的应用场景

2026年09月06日

25阅读

java中开关的定义,Java关键字有哪些

2026年09月06日

25阅读