DrissionPage解决滑动验证

之前爬取某数据统计平台时遇到了相当严重的反爬机制,采用普通的Selenium也无法绕过。之前尝试过undetected_chromedriver可以使用,但无法设置无头模式,使用起来还是有一定的不美观性。正好近日学习了Drission Page这款相当高效的工具,顺手掏出这个项目重构了一下。填输入数据相当简单:

import os
import cv2
import time
import random
from datetime import datetime
from DrissionPage import ChromiumPage
from DrissionPage import ChromiumOptions

input_code = ""

# 创建页面对象
co = ChromiumOptions().auto_port()  # 指定程序每次使用空闲的端口和临时用户文件夹创建浏览器
co.set_argument('--no-sandbox')  # 无沙盒模式
co.set_argument('--headless=new')  # 无界面系统添加
co.set_argument('--disable-gpu')    # 禁用gpu,提高加载速度
co.set_user_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36") 

# 创建浏览器窗口
webpage = ChromiumPage(co)


url = ""
webpage.get(url)
webpage.wait(5)

# 选择出口
out_span = webpage.ele('@text()=出口')
out_input = out_span.next('tag:input')
out_input.click()

# 选择币种
currency_span = webpage.ele('@text()=美元')
currency_input = currency_span.next('tag:input')
currency_input.click()

# 选择分月输出
by_month_input = webpage.ele('@name=monthFlag')
by_month_input.click()

# 起止年月
start_select = webpage.ele('@id=startMonth')
end_select = webpage.ele('@id=endMonth')
if start_select.child(-2):
    start_month_option = start_select.child(-2)
else:
    start_month_option = start_select.child(-1)
end_month_option = end_select.child(-1)
start_month_value = start_month_option.attr('value')
end_month_value = end_month_option.attr('value')
start_select.select.by_option(start_month_option)
end_select.select.by_option(start_month_option)

output_field_select_1 = webpage.ele('@id=outerField1')
output_field_select_2 = webpage.ele('@id=outerField2')
output_field_select_1.select.by_value("CODE_TS")
output_field_select_2.select.by_value("ORIGIN_COUNTRY")
output_field_input_1 = webpage.ele("@id=outerValue1")
output_field_input_1.input("85434000,85439090,24041100,24041200,24041990")


do_search_button = webpage.ele('@@tag()=a@@id=doSearch', timeout=2)
do_search_button.click()
time.sleep(1)
# webpage.get_screenshot('test_dp.png')

try:
    webpage.ele('xpath://div[@class="layui-layer-btn"]/a[@class="layui-layer-btn0"]', timeout=2).click()
except:
    pass
time.sleep(1)


这里进入到滑块验证界面,也算是一个难点:

参考了网上的一些方法,通过将缺口图片和背景图用cv2进行一些处理来获取滑动量。但是最后结果并不准确,研究后发现是因为缺口小图并不准确。以另一幅图为例,获取到的小图和背景分别是:

缺口小图
背景图

 可以发现直接通过get_screenshot()的方法获取到的小图并不是真的小图。暂时没发现准确获取小图的方法。转换了一下思路,只需要在背景图里找出面积较大的白色区域,就是缺口了。(滑动轨迹模拟的代码源于网络)

def find_blank_region(image):
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    _, binary = cv2.threshold(gray, 240, 255, cv2.THRESH_BINARY)
    contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    contours = [cnt for cnt in contours if cv2.contourArea(cnt) > 500]
    largest_contour = max(contours, key=cv2.contourArea)
    x, y, w, h = cv2.boundingRect(largest_contour)
    return x


# 滑动轨迹模拟 
def generate_track(distance):
    def __ease_out_expo(step):
        return 1 if step == 1 else 1 - pow(2, -10 * step)

    tracks = [[random.randint(20, 60), random.randint(10, 40), 0]]
    count = 30 + int(distance / 2)
    _x, _y = 0, 0
    for item in range(count):
        x = round(__ease_out_expo(item / count) * distance)
        t = random.randint(10, 20)
        if x == _x:
            continue
        tracks.append([x - _x, _y, t])
        _x = x
    tracks.append([0, 0, random.randint(200, 300)])
    times = sum([track[2] for track in tracks])
    return tracks, times

然后运用这两个函数拖动滑块完成验证。这里虽然设置了一直循环,但是大部分情况只需要一次就够了。

while True:
    background_canvas = webpage.ele('xpath://div[@id="captcha"]/canvas', timeout=2)
    background_canvas.get_screenshot('captcha.png')
    background_pic = cv2.imread('captcha.png')
    x_num = find_blank_region(background_pic) * 1.08 
    print(x_num)
    element = webpage.ele('xpath://div[@class="sliderMask"]/div[@class="slider"]')
    element.drag(offset_x=x_num/2, duration=1)
    # webpage.get_screenshot('test_move.png')
    msg = webpage.ele('xpath://div[@id="msg"]', timeout=2).text
    print(msg)
    if msg == '验证通过!':
        time.sleep(2)
        webpage.ele('xpath://div[@class="page-container"]//a[@id="doSearch"]', timeout=2).click()
        break
    time.sleep(3)


最后下载csv:

time.sleep(random.randint(6,8))
download_button = webpage.ele('@@tag()=span@@text()=导出数据')
download_button.click()
confirm_download_button = webpage.ele('@@tag()=a@@class=layui-layer-btn0')
mission = confirm_download_button.click.to_download(r'D:/', 'test2.csv')
mission.wait()
# webpage.get_screenshot('test_dp2.png')
from selenium import webdriver import time import random def parse_data(): divs = driver.find_elements_by_xpath('//div[@class="grid g-clearfix"]/div/div') # 所有的div标签 for div in divs: test = div.find_element_by_xpath('.//div[@class="row row-2 title"]/a').text # 商品名字 price = div.find_element_by_xpath('.//strong').text + '元' # 商品价格 deal = div.find_element_by_xpath('.//div[@class="deal-cnt"]').text # 付款人数 name = div.find_element_by_xpath('.//div[@class="shop"]/a/span[2]').text # 店铺名称 location = div.find_element_by_xpath('.//div[@class="location"]').text # 店铺地点 detail_url = div.find_element_by_xpath('.//div[@class="row row-2 title"]/a').get_attribute('href') # 详情页地址 print(test, price, deal, name, location, detail_url) if __name__ == '__main__': word = input('请输入要搜索的关键字:') # TODO 1、创建浏览器 driver = webdriver.Chrome() # TODO 2、修改了浏览器的内部属性,跳过了登录的滑动验证 driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {"source": """Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"""}) # TODO 3、执行浏览器操作 driver.get('https://www.taobao.com/') driver.implicitly_wait(10) # 智能化等待方法 driver.maximize_window() # 最大化 driver.find_element_by_xpath('//*[@id="q"]').send_keys(word) time.sleep(random.randint(1, 3)) driver.find_element_by_xpath('//*[@id="J_TSearchForm"]/div[1]/button').click() time.sleep(random.randint(1, 3)) """用户账号及密码登录""" driver.find_element_by_xpath('//*[@id="fm-login-id"]').send_keys('xxxxxx') # TODO 输入用户名 time.sleep(random.randint(1, 3)) driver.find_element_by_xpath('//*[@id="fm-login-password"]').send_keys('xxxxxxx') # TODO 输入密码 time.sleep(random.randint(1, 3)) driver.find_element_by_xpath('//*[@id="login-form"]/div[4]/button').click() time.sleep(random.randint(1, 3)) for page in range(0, 2): print(f'-----------------正在爬取第{page + 1}页-----------------') # TODO 调用商品解析的函数 parse_data() driver.find_element_by_xpath('//li[@class="item next"]/a[@class="J_Ajax num icon-tag"]').click() time.sleep(random.randint(2, 3))这是python的一个爬取淘宝的思路,请根据这个修改 crawler: timeout-ms: 10000 user-agent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" sources: amazon: "https://www.amazon.com/dp/%s" ebay: "https://www.ebay.com/itm/%s" selectors: amazon: "#corePriceDisplay span.a-price-whole" ebay: ".x-price-primary > span"配置确保能爬取到数据
03-10
<think>好的,我现在需要帮用户修改他们的Python爬虫代码,使其能够适配新的配置文件,从而爬取Amazon和Ebay的数据。首先,我得仔细看看用户提供的代码和配置。 用户原来的代码是用于爬取淘宝的,使用Selenium来模拟浏览器操作,包括登录、搜索、翻页等。现在他们希望根据提供的YAML配置来调整代码,以支持Amazon和Ebay的爬取。配置文件里指定了超时时间、User-Agent、数据源URL和选择器。 首先,我需要考虑如何将硬编码的淘宝部分替换为可配置的方式。原来的代码是针对特定网站的结构编写的,比如XPath选择器,而新的配置需要支持多个来源,每个来源有不同的URL和选择器。这意味着需要将爬虫通用化,根据不同的来源动态获取URL和选择器。 接下来,用户提供的配置中有sources和selectors,分别对应不同电商的URL模板和价格选择器。例如,Amazon的URL是https://www.amazon.com/dp/%s,其中%s可能是产品ID,而价格选择器是#corePriceDisplay span.a-price-whole。Ebay的类似。原来的代码中,URL是直接通过搜索关键词构建的,但现在可能需要根据产品ID来访问具体页面,所以爬取逻辑可能需要调整,从搜索改为直接访问产品页面。 然后,原来的代码使用了Selenium,但配置中提到的timeout和user-agent可能需要通过Selenium进行设置。例如,设置浏览器的User-Agent,以及页面加载的超时时间。用户之前的代码中已经有修改浏览器属性来绕过验证,这部分可能需要保留,同时添加User-Agent的设置。 另外,原代码中的parse_data函数是针对淘宝页面结构编写的,使用了特定的XPath来提取商品信息。现在需要根据不同的网站使用不同的选择器,可能需要重构parse_data函数,使其能够接受选择器参数,并根据不同的来源应用不同的选择器。 此外,原来的代码处理了登录流程,但Amazon和Ebay可能需要不同的登录方式,或者甚至不需要登录即可爬取公开的价格信息。需要确认是否需要保留登录步骤,或者将其移除,因为配置中的来源可能不需要登录。 还有一个问题是,原来的代码是输入关键字进行搜索,而新的配置中的URL模板使用了%s作为产品ID的占位符。这意味着爬虫可能需要输入产品ID列表,而不是关键字。因此,主程序的逻辑可能需要调整,从读取关键字改为读取产品ID,并循环访问每个来源的URL。 此外,配置中的timeout-ms是10000毫秒,也就是10秒,需要在Selenium中设置页面加载的超时时间。原来的代码使用了隐式等待driver.implicitly_wait(10),这应该和配置中的超时时间一致,可能需要调整参数。 用户代理的设置需要在Selenium启动浏览器时添加选项,修改User-Agent。原来的代码中使用了execute_cdp_cmd来修改navigator.webdriver属性,绕过检测,现在需要同时设置User-Agent,这可以通过ChromeOptions来实现。 然后,关于爬取逻辑,原来的代码是处理分页,但Amazon和Ebay的产品页面可能不需要分页,而是直接访问每个产品页面。因此,可能需要移除分页逻辑,改为循环遍历产品ID列表,构造URL,然后爬取每个页面的价格信息。 总结下来,修改的步骤可能包括: 1. 读取YAML配置文件,获取超时、User-Agent、来源和选择器。 2. 配置Selenium的选项,设置User-Agent和超时时间。 3. 根据配置中的来源,循环处理每个电商平台。 4. 对于每个平台,构造产品页面的URL,使用对应的选择器提取价格。 5. 移除原来的登录和搜索逻辑,改为直接访问产品页面。 6. 重构parse_data函数,使其能够根据不同的选择器提取数据。 7. 处理可能的反爬措施,如修改webdriver属性和随机等待时间。 需要注意的是,直接爬取Amazon和Ebay可能会遇到更严格的反爬机制,可能需要更多的措施,比如使用代理、随机延迟、headless模式等。此外,价格信息的提取可能需要处理动态加载的内容,确保选择器准确。 现在需要将上述思考转化为代码结构,确保配置正确加载,选择器动态应用,并且爬虫能够适应不同网站的结构。同时,要处理异常情况,比如元素未找到,页面加载超时等,保证爬虫的健壮性。</think>以下是基于你的淘宝爬虫代码及配置需求修改后的通用电商爬虫方案,适配 Amazon 和 eBay 的价格抓取: --- ### **修改后的代码结构与说明** #### 1. **配置文件 (`config.yml`)** ```yaml crawler: timeout-ms: 10000 user-agent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" sources: amazon: "https://www.amazon.com/dp/%s" ebay: "https://www.ebay.com/itm/%s" selectors: amazon: "#corePriceDisplay span.a-price-whole" ebay: ".x-price-primary > span" ``` #### 2. **Python 爬虫代码 (`main.py`)** ```python from selenium import webdriver from selenium.common.exceptions import NoSuchElementException import yaml import time import random class EcommerceCrawler: def __init__(self, config_path='config.yml'): # 加载配置文件 with open(config_path, 'r') as f: self.config = yaml.safe_load(f)['crawler'] # 初始化浏览器配置 self.options = webdriver.ChromeOptions() self.options.add_argument(f'user-agent={self.config["user-agent"]}') self.driver = webdriver.Chrome(options=self.options) # 设置超时与反检测 self.driver.set_page_load_timeout(self.config['timeout-ms'] / 1000) self.driver.execute_cdp_cmd(
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值