整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

Python爬虫的第二种姿势,Selenium框架案例讲解

章目录

  • selenium模块
  • selenium基本概念
  • 基本使用
  • 代码

. 为什么使用selenium

前面爬虫大多使用的是urllib 库, 另外还有requests 库。urllib 可以解决好多问题,包括代理、自定义请求体、cookie 设置等。 这些库都是模拟浏览器进行访问,毕竟是模拟,有些网站针对反爬会造成爬取的数据不准确。

这时候就需要引入selenium, selenuim 是真实的驱动浏览器,然后模拟人工操作去进行一系列的操作。

​ 举个简单的例子: 京东官网 https://www.jd.com/ 首页,如果通过网页访问我们能拿到秒杀模块; 如果是通过urllib 访问拿到首页源码,发现没有该模块。该模块所在区域的xpath 选择器是: //*[@id='J_seckill']

2. selenium 安装以及简单测试

1. 下载浏览器驱动

​ selenium 是驱动浏览器,然后去进行一系列的操作,所以我们需要下载对应的驱动。

​ chrome 驱动: http://chromedriver.storage.googleapis.com/index.html 根据自己的chrome 版本下载对应的驱动, 大版本一致即可,windows 安装包32 位也可以的。

​ 驱动下载完成后将解压后的exe驱动程序拷贝到运行程序同级目录,或者就放到python 目录然后加入到path 换变量中也可以。需要程序能找到该exe。

2. pycharm 安装selenium

File→Settings→Project:项目名称→python Interpreter->添加selenium

pycharm 安装完成后可以到项目的venv\lib 目录查看现有的模块

3. 本地安装selenium

如果是想python 跑对应脚本,需要本地安装selenium

pip install selenium

测试可以在python 窗口输入:

from selenium import webdriver

4. 简单测试

from selenium import webdriver

browser = webdriver.Chrome("chromedriver.exe")
browser.get("https://www.jd.com/")

# 获取网页源码
content = browser.page_source
print(content)
# 打印url
print(browser.current_url)

# 保存为图片(注意这里只能保存为png,不支持jpg)
browser.save_screenshot("jd.png")

# 打印title
print(browser.title)
# 窗口最大化
browser.maximize_window()

# 关闭
browser.close()

​ 会自动打开浏览器,然后访问京东的首页。然后打印网页源码(从源码可以搜索到id 为 J_seckill 的div)。最后将网页保存为图片后关闭浏览器。

3. selenium 解析以及获取

  1. 获取元素

获取元素主要有下面六个API:当然返回的元素还可以继续调用find_element 元素向下查找。

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

browser = webdriver.Chrome("chromedriver.exe")
browser.get("https://www.baidu.com/")

# 获取网页源码
# content = browser.page_source
# print(content)

'''
元素定位(改版后主要有两个方法)。 
两个方法参数一样
第一个指定模式: 参考 selenium.webdriver.common.by.By; 
class By:
    """
    Set of supported locator strategies.
    """

    ID = "id"
    XPATH = "xpath"
    LINK_TEXT = "link text"
    PARTIAL_LINK_TEXT = "partial link text"
    NAME = "name"
    TAG_NAME = "tag name"
    CLASS_NAME = "class name"
    CSS_SELECTOR = "css selector"
第二个指定属性的值 
find_element(by=By.ID, value=None)    查找单个元素,WebElement。 内部调用查找集合方法,然后返回第一个元素
find_elements(by=By.ID, value=None)     查找集合, WebElement 集合
'''

# 根据ID查找 (百度首页的百度一下按钮)
# button = browser.find_element(by=By.ID, value="su")
# print(button)

# 根据name属性查找 (查找百度首页的输入框)
# input = browser.find_element(by=By.NAME, value="wd")
# print(input)

# 根据xpath 语法查找 (查找百度首页的输入框)
# input = browser.find_element(by=By.XPATH, value="//input[@name='wd']")
# print(input)

# 根据标签的名字查找
# inputs = browser.find_elements(by=By.TAG_NAME, value="input")
# print(len(inputs))

# 根据link 内容查找 也就是根据<a>测试</a> 内部的text 属性查找
# a = browser.find_element(by=By.LINK_TEXT, value="新闻")
# print(a.text)

# css 选择器查找(找百度首页输入框)
a = browser.find_element(by=By.CSS_SELECTOR, value="#kw")
print(a.get_attribute("name"))

# 关闭
browser.close()
  1. 访问元素信息
from selenium import webdriver
from selenium.webdriver.common.by import By

browser = webdriver.Chrome("chromedriver.exe")
browser.get("https://www.baidu.com/")

# 根据link 内容查找 也就是根据<a>测试</a> 内部的text 属性查找
a = browser.find_element(by=By.LINK_TEXT, value="新闻")
# 获取属性
print(a.get_attribute("href"))
# 获取text
print(a.text)
# 获取tag
print(a.tag_name)

# 关闭
browser.close()
  1. 交互
点击: click()
输入: send_keys()
清空输入: clear()
后退:browser.back()
快进:browser.forward()
模拟JS滚动: js = 'document.body.scrollTop=100000'
		js = 'document.documentElement.scrollTop=100000'
		browser.execute_script(js) # 执行JS代码
获取网页源码: browser.page_source		

4. selenium 爬取百度

一个简单的例子,过程如下:

1.打开百度首页,睡眠2s

2.输入java,睡眠2s

3.点击百度一下按钮,睡眠5s

4.滚动到底部,睡眠5s

5.翻到第二页,睡眠5s

6.点击后退回退到第一页,睡眠5s

7.点击前进再回去第二页, 睡眠5s

8.结束

代码:

import time

from selenium import webdriver
# 1.打开百度首页,睡眠2s
from selenium.webdriver.common.by import By

browser = webdriver.Chrome("chromedriver.exe")
browser.get("https://www.baidu.com/")
time.sleep(2)

# 2.输入java,睡眠2s
input = browser.find_element(value="kw")
input.send_keys("java")
time.sleep(2)

# 3.点击百度一下按钮,睡眠5s
button = browser.find_element(value="su")
button.click()
time.sleep(5)

# 4.滚动到底部,睡眠5s
js = 'document.body.scrollTop=100000'
browser.execute_script(js)
time.sleep(5)

# 5.翻到第二页,睡眠5s
nextBtn = browser.find_element(by=By.XPATH, value="//a[@class='n']")
nextBtn.click()
time.sleep(5)

# 6. 点击后退回退到第一页,睡眠5s
browser.back()
time.sleep(5)

# 7. 点击前进再回去第二页, 睡眠5s
browser.forward()
time.sleep(5)

# 8. 关闭
browser.close()

5. selenium 不启动浏览器模式

打开浏览器再启动会浪费时间,对爬虫的性能也是个影响,还有一种就是不打开浏览器。

如下参数是针对chrome 的全局参数,不能自定义参数。

from selenium import webdriver

# 还有一些其他的参数
'''
# 添加UA
options.add_argument('user-agent="MQQBrowser/26 Mozilla/5.0 (Linux; U; Android 2.3.7; zh-cn; MB200 Build/GRJ22; CyanogenMod-7) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1"')

# 设置代理
options.add_argument("--proxy-server=http://110.52.235.176:9999") # 设置代理,请求头等,以列表的形式传入多个参数

# 设置编码格式
options.add_argument('lang=zh_CN.UTF-8') # 设置编码格式

 # 启动时最大化窗口
options.add_argument('--start-maximized')

# 指定浏览器分辨率
options.add_argument('window-size=1920x3000') 

# 谷歌文档提到需要加上这个属性来规避bug
options.add_argument('--disable-gpu') 

 # 隐藏滚动条, 应对一些特殊页面
options.add_argument('--hide-scrollbars')

# 不加载图片, 提升速度
options.add_argument('blink-settings=imagesEnabled=false') 

# 浏览器不提供可视化页面. linux下如果系统不支持可视化不加这条会启动失败
options.add_argument('--headless') 

# 以最高权限运行
options.add_argument('--no-sandbox')

# 手动指定使用的浏览器位置
options.binary_location = r"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" 

#添加crx插件
option.add_extension('d:\crx\AdBlock_v2.17.crx') 

# 禁用JavaScript
option.add_argument("--disable-javascript") 

# 设置开发者模式启动,该模式下webdriver属性为正常值
options.add_experimental_option('excludeSwitches', ['enable-automation']) 

# 禁用浏览器弹窗
prefs = {  
    'profile.default_content_setting_values' :  {  
        'notifications' : 2  
     }  
}  
options.add_experimental_option('prefs',prefs)
'''

option = webdriver.ChromeOptions()
# 浏览器不提供可视化页面. linux下如果系统不支持可视化不加这条会启动失败
option.add_argument("--headless")
# 谷歌文档提到需要加上这个属性来规避bug
option.add_argument('--disable-gpu')
browser = webdriver.Chrome(executable_path="chromedriver.exe", options=option)
browser.get("https://www.jd.com/")

# 获取网页源码
print(browser.title)

# 关闭
browser.close()

6. 以不打开浏览器的方式设置代理获取当前的IP信息

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

option = webdriver.ChromeOptions()
# 浏览器不提供可视化页面. linux下如果系统不支持可视化不加这条会启动失败
option.add_argument("--headless")
# 谷歌文档提到需要加上这个属性来规避bug
option.add_argument('--disable-gpu')
# 添加代理
option.add_argument("--proxy-server=http://221.11.233.111:4315")  # 设置代理,请求头等,以列表的形式传入多个参数

browser = webdriver.Chrome(executable_path="chromedriver.exe", options=option)
browser.get("http://www.ipdizhichaxun.com/")

# 获取网页源码
result = browser.find_element(by=By.XPATH, value='//*[@class="result"]')
print(result.text)

# 关闭
browser.close()

结果:

IP地址查询结果:221.11.233.111,IP地址位置:宁夏石嘴山市(联通)

7. 传cookie

import time

from selenium import webdriver

browser = webdriver.Chrome(executable_path="chromedriver.exe")
# 删除原来的cookie
browser.delete_all_cookies()
# 注意需要访问一次才能添加cookie。 selenium的默认域名为data,cookie中带域名,在设置cookie时发现当前域名不包含在cookie中,所以设置失败,一直都是data的这个页面
browser.get("http://127.0.0.1:8088/inner/t4")
# 携带cookie打开
browser.add_cookie({'name': 'ABC', 'value': 'DEF'})
browser.get("http://127.0.0.1:8088/inner/t4")
browser.refresh()

browser.close()
time.sleep(10)

8. 设置等待时间

​ 如果不设置等待时间,find_element会立即查找元素,找不到就会立即报错。一般会设置下时间,让JS渲染完成再找元素。

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

browser = webdriver.Chrome("chromedriver.exe")
# 设置等待时间,最长等待20 s。 不设置等待事件, find_element 找不到元素会抛出异常,设置完成之后不会报异常,会阻塞直到拿取到对应的元素或者达到指定时间
browser.implicitly_wait(20)
browser.get("http://baidu.com")
print(browser.find_element(by=By.XPATH, value='//div[@class="content"]//img'))

# 关闭
browser.close()

继续改进,找不到元素返回None 或者其他处理:

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

browser = webdriver.Chrome("chromedriver.exe")
# 设置等待时间,最长等待20 s。 不设置等待事件, find_element 找不到元素会抛出异常,设置完成之后不会报异常,会阻塞直到拿取到对应的元素或者达到指定时间
browser.implicitly_wait(20)
browser.get("http://baidu.com")

try:
    print(browser.find_element(by=By.XPATH, value='//div[@class="content"]//img'))
except Exception as e:
    # ignore
    print("没有元素")

# 关闭
browser.close()

9. selenuim 双击等事件

可以用ActionChains, 最后调用其perform() 方法就是提交事件

        # 找到第一个节点然后扩展
        node1 = browser.find_element(by=By.XPATH,value=' //*[name()="svg"]//*[name()="g"]/*[@class="node"][1]')
        # 调用ActionChains方法,生成一个动作
        # 创建一个ActionChains, 然后可以进行一系列事件、包括右键、双击、单击、拖拽、键盘按下、键盘抬起等事件
        action = ActionChains(browser)
        # 单击
        action.click(node1)
        # 双击
        # action.double_click(node1)
        # 右击
        action.context_click(node1)
        action.perform()

补充:
(1). 测试过程中发现browser 关闭之后,驱动程序不会关系,需要自己cmd 关闭下:

taskkill -im chromedriver.exe -f

(2). selenium 可以获取元素大小location、size、rect
这里需要注意获取到的元素的坐标是相对于浏览器内容区域左上角为原点,也就是
不包含头部浏览器菜单栏高度。

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

browser = webdriver.Chrome("chromedriver.exe")
browser.maximize_window()
browser.get("https://www.baidu.com/")

# css 选择器查找(找百度首页输入框)
a = browser.find_element(by=By.CSS_SELECTOR, value="#kw")
print(a.get_attribute("name"))
print(a.location)
print(a.size)
# rect = location + size
print(a.rect)

# 关闭
browser.close()

结果:

wd
{'x': 633, 'y': 222}
{'height': 44, 'width': 550}
{'height': 44, 'width': 550, 'x': 633, 'y': 222}

(3) 上面提到获取的元素的location是相对于浏览器可视区域的左上角为原点,也就是不包括菜单栏的高度。所以当和pyautogui 结合,自动进行鼠标操作的时候需要加上菜单栏高度:

    @staticmethod
    def middle_location(element: WebElement):
        ele_location = element.location
        ele_size = element.size
        middle_x = ele_location.get('x') + ele_size.get('width') / 2
        middle_y = ele_location.get('y') + ele_size.get('height') / 2 + ChromeBrowserContext.browser_bar_height()
        return middle_x, middle_y

    @staticmethod
    def browser_bar_height():
        '''
        获取当前broswer菜单栏高度
        
        :return: 
        '''
        # 执行js 并且拿到结果
        innerHeight = ChromeBrowserContext.__browser.execute_script("return window.innerHeight")
        outerHeight = ChromeBrowserContext.__browser.execute_script("return window.outerHeight")
        return outerHeight - innerHeight

(4) 结合pyautogui 实现拖拽方法

    @staticmethod
    def dragToByXpath(xpath1: str, xpath2: str, time: int):
        '''
        拖拽元素

        :param xpath1: 元素1
        :param xpath2:  元素2
        :param time:    时间
        :return:
        '''
        browser = ChromeBrowserContext.getBrowser()
        xpath1Element = browser.find_element(by=By.XPATH, value=xpath1)
        x, y = ChromeBrowserContext.middle_location(xpath1Element)
        pyautogui.moveTo(x, y)
        xpath2Element = browser.find_element(by=By.XPATH, value=xpath2)
        x, y = ChromeBrowserContext.middle_location(xpath2Element)
        pyautogui.dragTo(x, y, time)

    @staticmethod
    def dragToByElement(element1: WebElement, element2: WebElement, time: int):
        '''
        拖拽元素

        :param element1: 元素1
        :param element2:  元素2
        :param time: 时间
        :return:
        '''
        x, y = ChromeBrowserContext.middle_location(element1)
        pyautogui.moveTo(x, y)
        x, y = ChromeBrowserContext.middle_location(element2)
        pyautogui.dragTo(x, y, time)

在自己的测试过程中发现,selenium不支持动态修改header , 而且好像不能自定义header 进行请求。可以通过插件或者其他机制实现。

=selenium-wire 简单使用=

官网: https://github.com/wkeeling/selenium-wire 强烈建议直接使用这个库

​ selenium-wire 对selenium 做了扩展,让程序有能力拿到requests 对象,然后获取请求和响应信息,并且可以修改请求头(包括自定义的头)、修改请求参数(包括param和请求体中的JSON数据)、获取修改响应头等信息。

下面演示增加自己的header:(请求拦截器与响应拦截器用法)

from seleniumwire import webdriver


# 拦截request
def interceptor_request(request):
    del request.headers['mykey']  # Remember to delete the header first
    request.headers['mykey'] = 'mykey-value'  # Spoof the referer


# 拦截response
def interceptor_response(request, response):
    if request.url == 'http://localhost:8088/inner/t4':
        response.headers['New-Header'] = 'Some Value'


browser = webdriver.Chrome("chromedriver.exe")
browser.request_interceptor = interceptor_request
browser.response_interceptor = interceptor_response
browser.get("http://localhost:8088/inner/t4")

for request in browser.requests:
    if request.response:
        print(
            request.url,
            request.response.status_code,
            request.response.headers
        )

需要注意,进程结束之后browser 也是close 状态的,无法进行后续页面刷新或请求数据。

补充:

  1. selenium open新窗口或跳转页面无法定位元素
    因为窗口句柄还停留在上一个页面,所以导致无法定位元素。报错 “元素不可交互”。
    因此要解决的问题就是先定位到当前页面(也就是跳转后的页面)然后再进行元素的定位。

.selenium抓取动态网页数据基础介绍

1.1 什么是AJAX

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

  因为传统的网页在传输数据格式方面,使用的是XML语法,因此叫做AJAX,其实现在数据交互基本上都是使用JSON。使用AJAX加载的数据,即使使用了JS将数据渲染到了浏览器中,在右键->查看网页源代码还是不能看到通过ajax加载的数据,只能看到使用这个url加载的html代码。

1.2 获取ajax数据的方式

  法1:直接分析ajax调用的接口。然后通过代码请求这个接口。

  法2:使用Selenium+chromedriver模拟浏览器行为获取数据。

方式

优点

缺点

分析接口

直接可以请求到数据。不需要做一些解析工作。代码量少,性能高。

分析接口比较复杂,特别是一些通过js混淆的接口,要有一定的js功底。容易被发现是爬虫。

selenium

直接模拟浏览器的行为。浏览器能请求到的,使用selenium也能请求到。爬虫更稳定。

代码量多。性能低。

1.3 Selenium+chromedriver获取动态数据

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

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

1.4 安装Selenium和chromedriver

  1. 安装SeleniumSelenium有很多语言的版本,有java、ruby、python等。我们下载python版本的就可以了。
  2. pip install selenium
  3. 安装chromedriver:下载完成后,放到不需要权限的纯英文目录下就可以了。

1.5 快速入门

  现在以一个简单的获取百度首页的例子来讲下Seleniumchromedriver如何快速入门:

from selenium import webdriver


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


# 初始化一个driver,并且指定chromedriver的路径
driver = webdriver.Chrome(executable_path=driver_path)
# 请求网页
driver.get("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;q=0.9",
    "Connection":"keep-alive",
    "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like 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; charset=UTF-8",
    "Cookie":"_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=%7B%22distinct_id%22%3A%22169fb1bb2c41ea-04951e55adc96a-7a1437-2073600-169fb1bb2c58d0%22%2C%22%24device_id%22%3A%22169fb1bb2c41ea-04951e55adc96a-7a1437-2073600-169fb1bb2c58d0%22%7D; sajssdk_2015_cross_new_user=1; _putrc=4C5D2603888320CA; JSESSIONID=ABAAABAAADEAAFIB00F5DDE71D51610901CB9E0031812BA; login=true; unick=%E4%BC%8D%E6%99%93%E4%B8%BD; showExpriedIndex=1; showExpriedCompanyHome=1; showExpriedMyPublish=1; hasDeliver=49; gate_login_token=7b04a40da89145a1fbc90a3d719616d28c8b0a303344ac37; index_location_city=%E6%88%90%E9%83%BD; 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%3A%2F%2Fwww.baidu.com%2Fbaidu.php%3Fsc.Ks000001qLT2daZnZWIez3ktR_jhHue3tONZubxU9mivhxeuj-Fxrjg6NnVcKTp-GYJ_YRvrc9_yOJ4uV-IEpfnPazPz7ctjve1qlDokCDfHYo9PV0uDfTmN1OunNUcCRU-sJuR8RZz60PAXzfKybAdvuCxUedbt8aWtTjAdCCuO298TwT8zN1-T5EG3kgkOweg0DHGIbvP55IZbr6.DY_NR2Ar5Od663rj6tJQrGvKD7ZZKNfYYmcgpIQC8xxKfYt_U_DY2yP5Qjo4mTT5QX1BsT8rZoG4XL6mEukmryZZjzsLTJplePXO-8zNqrw5Q9tSMj_qTr1x9tqvZul3xg1sSxW9qx-9LdoDkY4QPSl81_4pqO24rM-8dQjPakb3dS5iC0.U1Yk0ZDqs2v4VnL30ZKGm1Yk0Zfqs2v4VnL30A-V5HcsP0KM5gK1n6KdpHdBmy-bIykV0ZKGujYzr0KWpyfqnWcv0AdY5HDsnHIxnH0krNtknjc1g1nsnHNxn1msnfKopHYs0ZFY5HDLn6K-pyfq0AFG5HcsP0KVm1Y3nHDYP1fsrjuxnH0snNtkg1Dsn-ts0Z7spyfqn0Kkmv-b5H00ThIYmyTqn0K9mWYsg100ugFM5H00TZ0qPWm1PHm1rj640A4vTjYsQW0snj0snj0s0AdYTjYs0AwbUL0qn0KzpWYs0Aw-IWdsmsKhIjYs0ZKC5H00ULnqn0KBI1Ykn0K8IjYs0ZPl5fK9TdqGuAnqTZnVUhC0IZN15Hnkn1fknHT4P1DvPHR1PW61P100ThNkIjYkPHRYP10LrHTkPjTY0ZPGujd9rAwBmhuWrj0snjDzrj0Y0AP1UHYsPbm3wWTsrH0srjwarDcz0A7W5HD0TA3qn0KkUgfqn0KkUgnqn0KlIjYs0AdWgvuzUvYqn7tsg1Kxn7ts0Aw9UMNBuNqsUA78pyw15HKxn7tsg1nkrjm4nNts0ZK9I7qhUA7M5H00uAPGujYknjT1P1fkrjcY0ANYpyfqQHD0mgPsmvnqn0KdTA-8mvnqn0KkUymqn0KhmLNY5H00uMGC5H00uh7Y5H00XMK_Ignqn0K9uAu_myTqnfK_uhnqn0KEIjYs0AqzTZfqnanscznsc100mLFW5HRdPj0Y%26word%3D%25E6%258B%2589%25E5%258B%25BE%25E7%25BD%2591%26ck%3D1701.10.72.227.558.354.602.254%26shh%3Dwww.baidu.com%26sht%3D62095104_19_oem_dg%26us%3D1.0.1.0.1.301.0%26bc%3D110101; PRE_LAND=https%3A%2F%2Fwww.lagou.com%2Flp%2Fhtml%2Fcommon.html%3Futm_source%3Dm_cf_cpc_baidu_pc%26m_kw%3Dbaidu_cpc_cd_e110f9_d2162e_%25E6%258B%2589%25E5%258B%25BE%25E7%25BD%2591; Hm_lvt_4233e74dff0ae5bd0a3d81c6ccf756e6=1553064535,1554696044,1554707975; TG-TRACK-CODE=index_search; SEARCH_ID=16b25888bc6f489f981996ef505d6930; X_HTTP_TOKEN=3704e5535eab672a10080745514b2c7fac0430c282; Hm_lpvt_4233e74dff0ae5bd0a3d81c6ccf756e6=1554708001; LGRID=20190408152002-b9743b5b-59ce-11e9-9a84-525400f775ce",
    "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
        response = requests.request(method='post',url=url,headers=headers,params = params,data = form_data)
        result = response.json()
        result_list = result['content']['positionResult']['result']
        for position in result_list:
            position_id = position['positionId']
            detail_url = 'https://www.lagou.com/jobs/%s.html'%position_id
            data = parse_detail_page(detail_url)
            datas.append(data)
            time.sleep(2)
    return datas


def parse_detail_page(url):
    resonse = requests.request(method='get',url=url,headers = headers)
    text = resonse.text
    html = etree.fromstring(text,parser=etree.HTMLParser())
    position_name = html.xpath('//span[@class="name"]/text()')[0].strip()
    detail_list = html.xpath('//dd[@class="job_request"]//span')
    salary = detail_list[0].xpath('text()')[0].strip()
    city = detail_list[1].xpath('text()')[0].strip()
    city = re.sub(r'[\s/]','',city)
    work_years = detail_list[2].xpath('text()')[0].strip()
    work_years = re.sub(r'[\s/]','',work_years)
    education = detail_list[3].xpath('text()')[0].strip()
    education = re.sub(r'[\s/]','',education)
    job_details = ''.join(html.xpath('//div[@class="job-detail"]//p//text()'))
    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
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import 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)
            source = self.driver.page_source
            WebDriverWait(driver=self.driver,timeout=10).until(EC.presence_of_element_located((By.XPATH,'//div[@class="pager_container"]/span[last()]')))
            self.parse_list_page(source)
            next_btn = self.driver.find_element_by_xpath('//div[@class="pager_container"]/span[last()]')
            if "pager_next_disabled"  in next_btn.get_attribute('class'):
                break
            else:
                next_btn.click()
            time.sleep(1)


    def parse_list_page(self,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)


    def request_detail_page(self,url):
        self.driver.execute_script("window.open('%s')"%url)
        self.driver.switch_to.window(self.driver.window_handles[1])
        WebDriverWait(self.driver,timeout=10).until(EC.presence_of_element_located((By.XPATH,'//span[@class="name"]')))
        source = self.driver.page_source
        self.parse_detail_page(source)
        #关闭当前详情页
        self.driver.close()
        #切换回职位列表页
        self.driver.switch_to.window(self.driver.window_handles[0])


    def parse_detail_page(self,source):
        html = etree.HTML(source)
        position_name = html.xpath('//span[@class="name"]/text()')[0].strip()
        detail_list = html.xpath('//dd[@class="job_request"]//span')
        salary = detail_list[0].xpath('text()')[0].strip()
        city = detail_list[1].xpath('text()')[0].strip()
        city = re.sub(r'[\s/]', '', city)
        work_years = detail_list[2].xpath('text()')[0].strip()
        work_years = re.sub(r'[\s/]', '', work_years)
        education = detail_list[3].xpath('text()')[0].strip()
        education = re.sub(r'[\s/]', '', education)
        desc = ''.join(html.xpath('//dd[@class="job_bt"]//text()')).strip()
        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自动化、接口、框架搭建学习资料!技术大牛解惑答疑,同行一起交流。