使用BS4和Selenium实现高级网页数据采集的实战指南(爬取知乎数据)
前言:
最近因为一些原因,需要收集一些知乎的数据进行分析。但当实际操作时却发现遇到了种种问题.首当其冲的就是知乎的反爬机制.最初我的思路是先手动登录,然后提取并存放cookie信息到本地以方便以后使用.然后重新打开后再读取cookie然后刷新页面,结果遇到了和这篇博文一样的情况.重磅!!!惊现知乎新反爬_知乎反爬-CSDN博客
虽然能成功登录和访问问答,但当我获取搜索结果和点击展开评论时会报错.
经过一段时间的查阅和尝试后写下了这篇博客.
最初的思路:
不完整代码如下:
使用Selenium来登录知乎,然后将获取到的cookies保存到一个JSON文件中,以便后续使用。在浏览器刷新时加载这些cookies,以实现自动登录。然而会遇到之前所说的那种问题,开始思考是哪里被发现了.
被发现的原因
经过查阅后发现,
Selenium容易被检测的主要原因在于它所创建的浏览器指纹与手动操作所创建的浏览器指纹不同。一个常见的检测方法是通过检查window.navigator.webdriver
这个关键字。在使用Selenium打开的浏览器中,该关键字的打印结果通常为true
,而在正常手动操作的浏览器中,打印结果则为undefined
。网站可以通过比较这些关键字的返回值来检测是否有自动化操作的迹象。这就是为什么Selenium容易被检测到的原因之一。
如何绕过
修改window.navigator.webdriver
关键字返回结果.
options = webdriver.ChromeOptions()
# 此步骤很重要,设置为开发者模式,防止被各大网站识别出来使用了Selenium
driver = webdriver.Chrome(options=options)
# js注入,修改webdriver返还
driver.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument', {
'source': 'Object.defineProperty(navigator, "webdriver", {get: () => undefined})'
})
driver.get('https://www.zhihu.com')
这段代码的关键点在于使用Chrome浏览器的开发者模式,同时通过CDP注入js,将navigator.webdriver
的返回值修改为undefined
。这样,网站在检测navigator.webdriver
时会返回undefined
,不再能够轻易检测到Selenium的使用。
然而,因为浏览器指纹很多,这种方法的局限性是显而易见的。不过配合手动登录,已经能获取服务器信任了.而是用cookie登录的话还是会出现之前的情况.
更多的应对机制
像之前提到的指令是在页面加载后执行js注入去修改特征,具有局限性,如果在修改之前网站就检查到该特征也还是会被发现。而参考:java - Selenium webdriver: Modifying navigator.webdriver flag to prevent selenium detection - Stack Overflow 有以下几条比较实用的方式:
-
options.add_experimental_option("excludeSwitches", ["enable-automation"])
:- 通过
add_experimental_option
方法添加了一个名为"excludeSwitches"的选项,其值为一个包含"enable-automation"的列表。 - 这个配置的目的是禁用Chrome中的"enable-automation"开关,这个开关通常用于检测自动化操作。
- 通过
-
options.add_experimental_option('useAutomationExtension', False)
:- 通过
add_experimental_option
方法添加了一个名为"useAutomationExtension"的选项,其值为False。 - 这个配置用于禁用自动化扩展,这可以防止网站检测到Selenium的自动化操作。
- 通过
-
options.add_argument("disable-blink-features=AutomationControlled")
:- 通过
add_argument
方法添加了一个参数,其值为"disable-blink-features=AutomationControlled"。 - 这个参数用于禁用Blink引擎中与自动化控制相关的特性,以进一步降低被检测为自动化爬虫的风险。
- 通过
或是直接通过cmd打开一个浏览器,再通过远程连接的方式去操作,这样无论打开多少个页面也不会被检测到.
# Author: 冷月半明
# Date: 2023/10/13
# Description: This script does XYZ.
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
from selenium.webdriver.chrome.options import Options
import time
import json
from bs4 import BeautifulSoup
import subprocess
# 远程连接的方式建立浏览器实例
cmd = '"C:\Users\20976\AppData\Local\Google\Chrome\Application\chrome.exe" --remote-debugging-port=9222 --user-data-dir="D:\code\Python大数据可视化\selenium_city\ChromeProfile"'
subprocess.run(cmd)
chrome_options = Options()
chrome_options.add_experimental_option("debuggerAddress", "127.0.0.1:9222")
driver = webdriver.Chrome(options=chrome_options)
driver.get('https://www.zhihu.com')
cmd里第一个参数为浏览器可执行文件位置,第二个参数为占用端口,第三个参数为生成的数据文件存放位置.
自动化操作
def finddata():
input = driver.find_element(By.ID,'Popover1-toggle')
input.send_keys('南阳市旅游景点')
time.sleep(3)
element =driver.find_element(By.CLASS_NAME, 'Popover')
button= wait.until(EC.visibility_of_element_located((By.TAG_NAME, 'button')))
# element.click()
# = element.find_element(By., 'button')
button.click()
time.sleep(5)
首先查找了一个具有特定ID的输入框,然后在输入框中输入了文本。接着,它等待页面加载,查找了一个具有特定CLASS_NAME的元素,然后使用ExpectedConditions
等待,确保一个按钮元素可见,并点击该按钮。
bs4提取数据
def gethtml():
# 获取网页源代码
page_source = driver.page_source
# 使用 BeautifulSoup 解析源代码
soup = BeautifulSoup(page_source, 'html.parser')
# print(soup)
# print(soup.select('.entry-list .item .entry .content-wrapper .content-main .title-row'))
for child in soup.select('.ListShortcut .css-0 .ContentItem-title'):
print(child.find('div').find('a')['href'],child.find('div').find('span').text)
首先使用driver.page_source
获取网页的HTML源代码,然后使用BeautifulSoup库来解析HTML。它遍历了具有特定CSS选择器的元素。在循环中,代码使用.find()
方法来查找特定元素,然后提取链接和文本信息,并将它们打印出来。
成果展示:
爬取到的url和标题.
转载自:https://juejin.cn/post/7289397324429066297