Skip to content

爬虫学习记录(下)

3905字约13分钟

Spider

2024-03-15

如何查看函数参数和方法介绍

  • ctrl+鼠标左键点击函数

Selenium --- 过验证大杀器

pip install selenium

驱动安装-chrome

注意:若浏览器系统版本更新,需要更换chromedriver驱动

  1. chrome浏览器输入chrome://version/查看浏览器系统版本,驱动版本匹配优先最近的版本

  2. 进入Chrome for Testing 并选择stable(稳定版)

  3. 选择适合自己平台的chromedriver并复制链接进行下载解压并提取chromedriver文件(若仅有win32,则win32包含win64,下载即可)

image-20240220011108096
  1. 法一:终端输入py -op查看python解释器路径,并把chromedriver移动到路径中
image-20240220014046507
from selenium.webdriver import Chrome, Firefox, Ie, Safari, Edge

# 创建浏览器对象
web = Chrome()
url = "http://www.baidu.com"
web.get(url)
  1. 法二:将chromedriver移动到工程中,并在代码中加入chromedriver路径
image-20240220015725651
# 简易版本--速度最快--因为驱动路径查找优先
from selenium.webdriver import Chrome
from selenium.webdriver.chrome.service import Service

# 若需要无头浏览器,则解开以下两条代码
options = Options()
# options.add_argument('--headless')
# options.add_argument("--disable-gpu") 

# 启动Chrome浏览器,并传入参数
web = Chrome(service=Service("./chromedriver.exe"), options=options)
web.implicitly_wait(10)

web.get("http://www.baidu.com")

导入selenium

from selenium.webdriver import Chrome, Firefox, Ie, Safari, Edge

浏览器配置

设置驱动路径

from selenium.webdriver import Chrome
from selenium.webdriver.chrome.service import Service

# 设置Chrome驱动程序路径
chrome_service = Service("./chromedriver.exe")
# 启动Chrome浏览器,并传入驱动路径
web =Chrome(service=chrome_service)

SSL验证

options = Options()
options.add_argument("--allow-running-insecure-content")
options.add_argument("--ignore-certificate-errors")

无头浏览器

from selenium.webdriver import Chrome
from selenium.webdriver.chrome.service import Service

# 浏览器配置
options = Options()
# 无头浏览器
options.add_argument('--headless')     
# 禁用gpu,GPU加速会让浏览器的性能更好,但是会增加一些特征点,从而被检测出来
options.add_argument("--disable-gpu")                  
web = Chrome(service=Service("./chromedriver.exe"), options=options)

防检测

防检测方法非万能,对于特定网站:“见人说人话,见鬼说鬼话”,分析网站检测方式,针对性解决

UA检测

UA过短---selenium默认的user-agent比较短,这就可能会让部分网站检测出我们使用了selenium,增加了这一特征点被检测出来的概率。我们可以修改user-agent来解决这个问题

options.add_argument('--headless')
user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 S```afari/537.36'
options.add_argument(f'user-agent={user_agent}')
浏览器分辨率检测

selenium无头模式下的浏览器分辨率默认是800*600,这个分辨率太小了,很容易被检测出来,我们可以设置一个大一点的分辨率来解决这个问题

options.add_argument('--headless')
# 参数设置分辨率
options.add_argument("--window-size=1920,1080")

web = Chrome(options=options)
# 浏览器大小分辨率
browser.set_window_size(1920, 1080)
GPU加速

selenium无头模式下的浏览器默认是开启GPU加速的,我们可以禁用GPU加速来解决这个问题,GPU加速会让浏览器的性能更好,但是会增加一些特征点,从而被检测出来

options.add_argument('--headless')
options.add_argument("--disable-gpu")
JS检测webdriver

有些网站会通过js来检测是否使用了selenium,在启用selenium后js读取window.navigator.webdriver参数返回值为true,这样就可以检测出我们使用了selenium。我们可以通过关闭浏览器上部提示语来修改js特征

# 88版本以前
web.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
  "source": """
    Object.defineProperty(navigator, 'webdriver', {
      get: () => undefined
    })
  """
})

# 88版本以后
options = Options()
options.add_argument("--disable-blink-features=AutomationControlled")

其他配置

常用配置
# 无头浏览器
options.add_argument('--headless')    
# 禁用gpu,GPU加速会让浏览器的性能更好,但是会增加一些特征点,从而被检测出来
options.add_argument("--disable-gpu")       
# 配置对象添加替换User-Agent的命令
options.add_argument('--user-agent=Mozilla/5.0 HAHA')
# 设置浏览器分辨率(窗口大小)
options.add_argument('--window-size=1366,768')       
# 最大化运行(全屏窗口),不设置,取元素会报错
options.add_argument('--start-maximized')     
# 禁用浏览器正在被自动化程序控制的提示
options.add_argument('--disable-infobars')          
# 隐身模式(无痕模式)
options.add_argument('--incognito')             
# 禁用javascript
options.add_argument('--disable-javascript')
全部配置

List of Chromium Command Line Switches « Peter Beverloo

基本操作

  • 创建浏览器实例化对象

    web = Chrome()	# 如果驱动在工程中,要在括号加上路径
  • 打开网址

    url = "https://www.lagou.com/wn/"
    web.get(url)
  • 页面截图到路径

    web.find_element(By.XPATH, '//form/div/img').screenshot("文件名.jpg")	# 保存到文件中
    png = web.find_element(By.XPATH, '//form/div/img').screenshot_as_png()	# 保存到文件,返回值为字节
    base64 = web.find_element(By.XPATH, '//form/div/img').screenshot_as_base64()	# 图片转化为base64编码返回字符串
  • 关闭窗口

    web.close()
  • 退出整个浏览器

    web.quit()
  • 获取cookies

    爬虫学习记录(下)->笔记本->selenium获取cookies

定位、操作、提取

定位

from selenium.webdriver.common.by import By

web.find_element(By.方法名, 定位字符串)		# element不加s是获取第一个节点
web.find_elements(By.方法名, 定位字符串)	# elements是获取一组符合条件的节点
  • 八种方法-----element + s即为获取一组符合条件的节点
函数阐释例子
By.ID根据id属性值定位web.find_element(By.ID, "id属性值")
By.XPATH根据xpath语句定位web.find_element(By.XPATH, "xpath语句")
By.TAG_NAME根据标签名定位web.find_element(By.TAG_NAME, "标签名")
By.LINK_TEXT根据超链接文本定位web.find_element(By.LINK_TEXT, "字符串")
By.PARTIAL_LINK_TEXT根据超链接文本定位web.find_element(By.PARTIAL_LINK_TEXT, "部分超链接文本")
By.NAME根据name属性值定位web.find_element(By.NAME, "name属性值")
By.CLASS_NAME根据class属性值定位web.find_element(By.CLASS_NAME, "class属性值")
By.CSS_SELECTOR根据css选择器值定位web.find_element(By.CSS_SELECTOR, "css选择器语句")

操作

  • 输入和点击
函数阐释例子
.send_keys("字符串")输入文字web.find_element(By.ID, "search_input").send_keys("爬虫", "JAVA")
.send_keys("Keys.按键")输入按键【详情查看函数方法】web.find_element(By.ID, "search_input").send_keys("Keys.ENTER")
.click()点击web.find_element(By.ID, "search_button").click()
  • 拖动和滑动

    from selenium.webdriver import Chrome, ActionChains     # 事件链
    # 拖拽
    ActionChains(web).drag_and_drop_by_offset(节点, xoffset=偏移坐标X, yoffset=偏移坐标Y).perform()
    # 点击抓住-拖动
    ActionChains(web).click_and_hold(节点).move_by_offset(xoffset=偏移坐标X, yoffset=偏移坐标Y).perform()
    # 休眠
    ActionChains(web).pause(random.uniform(0.0001, 0.0003)) # 事件休眠

提取节点数据

函数阐释例子
.text提取text值print(web.find_element(By.XPATH, '//*[@id="job_detail"]/dd[2]/div').text)
.get_property('属性名')提取属性值print(web.find_element(By.XPATH, '//*[@id="job_detail"]/dd[2]/div').get_property('href'))

下拉列表的选择

导包
from selenium.webdriver.support.select import Select
定位下拉列表
sel_new = Select(web.find_element(By.XPATH, '//*[@id="OptionDate"]'))
切换选项
函数阐释例子
.select_by_index(索引值)通过索引位置切换sel_new.select_by_index(5)
.select_by_value('指定value值')通过value值切换sel_new.select_by_value('河南')
.select_by_visible_text('指定可见文字')通过可见文字切换sel_new.select_by_visible_text('河南省')
示例
from selenium.webdriver import Chrome
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.select import Select
import time

# 配置无头浏览器
options = Options()
# options.add_argument("--headless")
# options.add_argument("--disable-gpu")
options.add_argument("--window-size=1920,1080")

# 启动Chrome浏览器,并传入参数
web = Chrome(service=Service("./chromedriver.exe"), options=options)
web.set_window_size(1920, 1080)

web.get("https://www.endata.com.cn/BoxOffice/BO/Day/index.html")
time.sleep(5)

# 定位到select
sel_new = Select(web.find_element(By.XPATH, '//*[@id="OptionDate"]'))

print(len(sel_new.options))  # 所有的选项

for i in range(len(sel_new.options)):
    sel_new.select_by_index(i)
    time.sleep(3)
    tr_list = web.find_elements(By.XPATH, '//*[@id="TableList"]/table/tbody/tr')
    for tr in tr_list:
        print(tr.find_element(By.XPATH, './td[2]/a/p').text)

time.sleep(2000)

获取页面信息

函数阐释例子
web.title获取网站的titleprint(web.title)
web.page_source获取页面代码【已经通过script脚本更新过】print(web.page_source)
web.getcookie("cookie字典中的指定键")获取指定cookie值print(web.get_cookies("cookie字典中的指定键"))
web.get_cookies()获取cookie字典-所有cookiesprint(web.get_cookies())
web.current_url获取当前url地址print(web.current_url)

切换驱动器视角

  • 切换窗口

    • 切换到第一个窗口

      web.switch_to.window(web.window_handles[0])
    • 切换到最后一个窗口

      web.switch_to.window(web.window_handles[-1])
  • 切换框架【页面内嵌套ifame---登录】

    • 切入指定iframe

      iframe = web.find_element(By.XPATH, '//*[@id="kw"]')
      web.switch_to.frame(iframe)
    • 切出iframe到其父框架

      web.switch_to.parent_frame()

等待处理

  • time等待

    time.sleep(10)	# 等10s,无论加不加载出来
  • 隐式等待--最多等x秒--一次设置,全局使用

    web.implicitly_wait(10)	# 最多等待时间
  • 显式等待--要等待的节点--多次设置

    webDriveWait(,10,0.5)	# 最多等待时间,查找频率
    
    from selenium.webdriver.support.ui import WebDriverWait     # 第三种等待方法
    from selenium.webdriver.support import expected_conditions as EC    # 等待出现的对象
    el = WebDriverWait(web, 10, 0.5).until(
        EC.presence_of_element_located((By.XPATH, 'xpath语句'))
    )

DrissionPage

欢迎 | DrissionPage (gitee.io)

安装

pip install DrissionPage	# 安装
pip install DrissionPage --upgrade	# 升级到稳定版

导入

from DrissionPage import ChromiumPage	# 浏览器模式
from DrissionPage import SessionPage	# 数据包模式
from DrissionPage import WebPage		# 全部都有

from DrissionPage import ChromiumOptions	# 浏览器对象启动参数
from DrissionPage import SessionOptions		# session对象启动参数
from DrissionPage.common import Settings	# 全局配置,找不到元素抛出异常

from DrissionPage.common import Keys	# 按键
from DrissionPage.common import Actions	# 动作链【浏览器对象已经内置】
from DrissionPage.common import By		# 与selenium一致

from DrissionPage.common import wait_until			# 等待传入方法结果为真
from DrissionPage.common import make_session_ele	# 从 html 文本生成ChromiumElement对象
from DrissionPage.common import configs_to_here		# 把配置文件复制到当前路径
from DrissionPage.common import get_blob			# 获取指定的blob资源

操作

# 等待页面加载
page.wait.load_start()

接码平台

椰子云API

自建代理

docker pull sameersbn/squid # 1.拉取squid镜像

标准配置

vim squid/squid.conf	# 2.squid.conf配置
==============================
acl all src 0.0.0.0/0.0.0.0
acl SSL_ports port 443
acl Safe_ports port 80 # http
acl Safe_ports port 443 # https
acl CONNECT method CONNECT
http_access allow all
http_port {port}
visible_hostname proxy
==============================
# 3.启动容器[挂载squid.conf]
sudo docker run \
	-d \
	--name squid \
	-p 3128:3128 \
	-v /home/spa/squid/squid.conf:/etc/squid/squid.conf \
	sameersbn/squid
# 4.测试
export http_proxy=http://{代理IP}:3128
wget http://www.baidu.com

账号密码配置

# 1.安装软件包并生成账号密码文件[公网服务器上]
sudo apt install apache2-utils
sudo htpasswd -c .squid_users {account_name}	# 然后输入account_password
# 2.squid.conf配置
vim squid/pass_squid.conf
=================================================================================
acl localnet src 0.0.0.1-0.255.255.255	# RFC 1122 "this" network (LAN)
acl localnet src 10.0.0.0/8		# RFC 1918 local private network (LAN)
acl localnet src 100.64.0.0/10		# RFC 6598 shared address space (CGN)
acl localnet src 169.254.0.0/16 	# RFC 3927 link-local (directly plugged) machines
acl localnet src 172.16.0.0/12		# RFC 1918 local private network (LAN)
acl localnet src 192.168.0.0/16		# RFC 1918 local private network (LAN)
acl localnet src fc00::/7       	# RFC 4193 local private network range
acl localnet src fe80::/10      	# RFC 4291 link-local (directly plugged) machines

acl SSL_ports port 443
acl Safe_ports port 80  # http
acl Safe_ports port 21  # ftp
acl Safe_ports port 443 # https
acl Safe_ports port 70  # gopher
acl Safe_ports port 210 # wais
acl Safe_ports port 1025-65535  # unregistered ports
acl Safe_ports port 280 # http-mgmt
acl Safe_ports port 488 # gss-http
acl Safe_ports port 591 # filemaker
acl Safe_ports port 777 # multiling http
acl CONNECT method CONNECT

# username&password auth config
auth_param basic program /usr/lib/squid/basic_ncsa_auth /etc/squid/squid_passwd
acl ncsa_users proxy_auth REQUIRED
http_access allow ncsa_users

http_access deny !Safe_ports
http_access deny CONNECT !SSL_ports
http_access allow localhost manager
http_access deny manager
http_access deny to_localhost
http_access allow localnet
http_access allow localhost
http_access deny all
http_port 3128

refresh_pattern ^ftp:		1440	20%	10080
refresh_pattern ^gopher:	1440	0%	1440
refresh_pattern -i (/cgi-bin/|\?) 0	0%	0
refresh_pattern (Release|Packages(.gz)*)$ 0 20% 2880
refresh_pattern .		0	20%	4320
=================================================================================
# 3.启动容器[挂载squid.conf、squid_passwd]
docker run \
	-d \
	--name squid \
	-p 3128:3128 \
	-v /home/spa/squid/pass_squid.conf:/etc/squid/squid.conf \
	-v /home/spa/squid/.squid_users:/etc/squid/.squid_users \
	sameersbn/squid
# 4.测试
export http_proxy=http://{account_name}:{account_password}@{代理IP}:{3128}
wget http://www.baidu.com

实际使用

自定义:

  1. 端口及防火墙开放此端口
  2. 账号密码

字体反爬

字体格式

  1. TureTpe(.ttf)格式

Windows和Mac的最常见的字体,是一种RAW格式,因此他不为网站优化

  • 支持的浏览器:IE9+、Firefox3.5+、Chrome4+、Safari3+、Opera10+、iOS Mobile Safari4.2+
  1. OpenType(.otf)格式

原始的字体格式,内置在TureType的基础上,提供更多的功能

  • 支持的浏览器:Firefox3.5+、Chrome4.0+、Safari3.1+、Opera10.0+、iOS Mobile Safari4.2+
  1. Web Open Font Format(.woff)格式

Web字体中最佳格式,兼容性最好,开放的TrueType/OpenType的压缩版本,支持元数据包的分离

  • 支持的浏览器:IE9+、Firefox3.5+、Chrome6+、Safari3.6+、Opera11.1+
  1. Embedded Open Type(.eot)格式

IE专用字体,可从TrueType创建此格式字体

  • 支持的浏览器:IE4+
  1. SVG(.svg)格式

基于SVG字体渲染的一种格式

  • 支持的浏览器:Chrome4+、Safari3.1+、Opera10.0+、iOS Mobile Safari3.2+

字体渲染

通过css的@font-face属性进行渲染

@font-face {
     font-family: <YourWebFontName>;	
     src: <source> [<format>][,<source> [<format>]]*;
     [font-weight: <weight>];
     [font-style: <style>];
 }
/*
    YourWebFontName:自定义的字体名称,将被引用到你的Web元素中的font-family。如“font-family:“YourWebFontName”;”
    source:自定义的字体的存放路径,可以是相对路径也可以是绝路径;
   	format:自定义的字体的格式,主要用来帮助浏览器识别,其值主要有以下几种类型:truetype,opentype,truetype-aat,embedded-opentype,avg等;
    weight:定义字体是否为粗体
	style:定义字体样式,如斜体。
    [ ]:代表参数可不填。
*/

字体文件操作软件

  • Windows:FontCreator
  • macOS:IconFronPreview
  • Linux:FontForge

解密流程

  1. 下载woff文件
  2. 使用FontCreator查看密文的unicode值与woff中特定值(如:name)的对应关系
  3. 映射关系
    • 进制等线性关系-->算法得出映射关系字典
    • 汉字等难以查看线性关系
      1. 使用fontTools将woff文件特定值对应的字形转为jpg图片
      2. OCR识别图片并构建映射关系字典
  4. 加密文本解密

验证方法

点选验证

使用事件链

from selenium.webdriver import Chrome, ActionChains     # 事件链

for p in 验证返回值:		# 验证返回值示例:12,05|256,895
    x = p.split(',')[0]
    y = p.split(',')[1]
    ActionChains(web).move_to_element_with_offset(verify, xoffset=x, yoffset=y).click().perform()    # perform():提交事件
    time.sleep(1)
from selenium.webdriver import Chrome, ActionChains     # 事件链

# 使用图鉴打码平台做演示
def base64_api(img, typeid, uname='图鉴用户名', pwd='图鉴密码'):
    data = {"username": uname, "password": pwd, "image": img, "typeid": typeid}
    result = requests.post("http://api.ttshitu.com/predict", json=data).json()
    if result['success']:
        return result["data"]["result"]
    else:
        # !!!!!!!注意:返回 人工不足等 错误情况 请加逻辑处理防止脚本卡死 继续重新 识别
        return result["message"]
        
verify = web.find_element(By.XPATH, '/html/body/div[5]/div[2]/div[6]')  # 点选验证图片的全部框
# verify.screenshot('123.png')

# print(base64_api(verify.screenshot_as_base64, 27))

for p in base64_api(verify.screenshot_as_base64, 27).split('|'):
    x = p.split(',')[0]
    y = p.split(',')[1]
    ActionChains(web).move_to_element_with_offset(verify, xoffset=x, yoffset=y).click().perform()    # perform():提交事件
    time.sleep(1)

笔记本

selenium获取cookies

cookies中有许多cookie,通过web.get_cookies()即可获取由许多cookie字典组成的一个cookies列表,然后经过拆分重组为需要的cookies。image-20240221145637499

from selenium.webdriver import Chrome
from selenium.webdriver.chrome.service import Service
from rich import print
import requests
import time


def get_cookies(delay, url):
    # 启动Chrome浏览器,并传入参数
    web = Chrome(service=Service("./chromedriver.exe"))

    web.get(url)
    time.sleep(delay)
    cookies = {}
    for cookie in web.get_cookies():
        cookies[cookie['name']] = cookie['value']

    return cookies


delay = 5  # 看网络性能填写,网越快,值越低
url = "http://www.baidu.com"
url1 = "想拿着cookies访问的链接"

resp = requests.get(url=url1, cookies=get_cookies(delay, url))
print(resp.text)

selenium执行js代码

web.execute_script("""
    var a = document.getElementsByClassName("un-login-banner")[0];
    a.parentNode.removeChild(a);
""")

selenium12306登录cookies

import requests
from selenium.webdriver import Chrome
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
import time
from rich import print
from rich.prompt import Prompt


username = '用户名'
password = '密码'
id_card_4 = '身份证后六位'

options = Options()
options.add_argument("--disable-blink-features=AutomationControlled")
options.add_experimental_option('detach', True)
options.add_argument('--start-maximized')

web = Chrome(service=Service('./chromedriver.exe'), options=options)
web.implicitly_wait(10)
web.get('https://kyfw.12306.cn/otn/resources/login.html')

web.find_element(By.XPATH, '//*[@id="toolbar_Div"]/div[2]/div[2]/ul/li[1]/a').click()
time.sleep(1)
web.find_element(By.XPATH, '//*[@id="J-userName"]').send_keys(username)
web.find_element(By.XPATH, '//*[@id="J-password"]').send_keys(password)


web.find_element(By.XPATH, '//*[@id="J-login"]').click()

web.find_element(By.XPATH, '//*[@id="id_card"]').send_keys(id_card_4)
web.find_element(By.XPATH, '//*[@id="verification_code"]').click()  # 获取验证码按钮
web.find_element(By.XPATH, '//*[@id="code"]').send_keys(Prompt.ask("请输入你的手机验证码"))
web.find_element(By.XPATH, '//*[@id="sureClick"]').click()
print(web.get_cookies())

time.sleep(2000)

细节

重定向

特征

1708514824282

  1. 状态码为302
  2. 响应头的location中含有新定向地址

如何处理

  • 定向方面:使用session可无视定向
  • 多层加密(如boss直聘):使用selenium直接无视(减慢速度),只是速度慢。

网站也有针对selenium的检测,若登陆账号可减免部分检测(有可能被封号)