Python——DrissionPage 学习笔记

一、练习网址

1   Drissionpage 4.0旧文档    https://drissionpage.cn/DP40Docs/
2   humanMouse.py  
3   绿色版谷歌浏览器   https://download-chromium.appspot.com/
4   格式化工具  https://spidertools.cn/
5   GT-Standard-Mono-VF-Beta-V1    
6   Cookie Manager浏览器插件    https://www.wikipedia.org
7   汇文明朝字体 https://github.com/wxhzhwxhzh/huiwenmincho-improved
8   银河录像局  https://nf.video/
9   CDP谷歌文档    https://chromedevtools.github.io/devtools-protocol/1-3/Network/
10  虫术 https://spiderbox.cn/
11  AichatOS   https://x.cat.caichatosb.com/
12  Vscode https://code.visualstudio.com/
13  骚神网    https://wxhzhwxhzh.github.io/saossion_code_helper_online/
14  现状JS教程 https://zh.javascript.info/
15  爬虫练习场  https://scrape.center/

二、模块测试

2.1 安装模块

pip install drissionpage -i https://mirrors.aliyun.com/pypi/simple/

#日志模块

pip install loguru -i https://mirrors.aliyun.com/pypi/simple/

准备谷歌浏览器

2.2测试模块

#https://drissionpage.cn/browser_control/browser_options
from DrissionPage import Chromium

browser = Chromium()

tab = browser.new_tab('https://www.baidu.com')

input('Press Enter to continue...')

运行正常会跳转到谷歌浏览器页面

2.3 浏览器启动设置

from DrissionPage import Chromium
from DrissionPage import ChromiumOptions

#创建浏览器启动对象
options = ChromiumOptions()

#配置浏览器启动选项

#设置浏览器路径
options.set_browser_path(r'C:\Program Files\Google\Chrome\Application\chrome.exe')
options.ignore_certificate_errors()  #忽略证书错误
options.no_imgs() #禁用图片
options.headless(False) #无头模式
options.set_local_port(9696)  #设置浏览器debug端口

#连接浏览器并获取浏览器对象
browser = Chromium(options)

#获取标签页对象并打开网页
tab = browser.new_tab('https://www.baidu.com')

三、基本操作

3.1 标签页设置

from DrissionPage import Chromium

browser = Chromium()
#获取标签页对象并打开网站
tab1 = browser.new_tab('https://www.baidu.com')
tab2 = browser.new_tab('https://www.bing.com')
tab3 = browser.latest_tab  #最新激活的网页
print(tab1.title, id(tab1))
print(tab2.title, id(tab2))
print(tab3.title, id(tab3))

browser.latest_tab.close()  #关闭最新标签页

#标签页没有焦点概念,多个可以并行操作,同时打开多个标签页

from concurrent.futures import ThreadPoolExecutor

def open_url(browser,url):
    browser.new_tab(url)

chinese_websites = [
    'https://www.taobao.com',
    'https://www.tmall.com',
    'https://www.jd.com',
]

#使用线程池
with ThreadPoolExecutor(max_workers=3) as executor:
    for url in chinese_websites:
        executor.submit(open_url,browser,url)

一次性打开四个网页

这段代码展示了如何使用 DrissionPage 库的 Chromium 浏览器自动化操作多个标签页,并结合线程池实现并发打开多个网站。主要功能包括:

代码功能概述

这段代码展示了如何使用 DrissionPage 库的 Chromium 浏览器自动化操作多个标签页,并结合线程池实现并发打开多个网站。

  1. 初始化 Chromium 浏览器实例
  2. 依次打开百度、必应等网站
  3. 操作和管理标签页(获取最新标签页、关闭标签页)
  4. 使用线程池并发打开多个中国电商网站

代码详细解读

1. 导入模块与初始化浏览器

from DrissionPage import Chromium

browser = Chromium()

Chromium 类用于控制 Chromium 内核浏览器(如 Chrome、Edge)

创建浏览器实例后,会自动启动一个浏览器窗口

2. 打开多个标签页并管理

# 获取标签页对象并打开网站
tab1 = browser.new_tab('https://www.baidu.com')
tab2 = browser.new_tab('https://www.bing.com')
tab3 = browser.latest_tab  # 最新激活的网页

# 打印标签页标题和ID(内存地址)
print(tab1.title, id(tab1))
print(tab2.title, id(tab2))
print(tab3.title, id(tab3))

# 关闭最新标签页
browser.latest_tab.close()

new_tab(url) 方法用于打开新标签页并访问指定 URL

latest_tab 属性返回当前最新激活的标签页

每个标签页对象有唯一的 ID(通过 id() 函数获取)

close() 方法关闭当前标签页

3. 并发打开多个网站

from concurrent.futures import ThreadPoolExecutor

def open_url(browser, url):
    browser.new_tab(url)

chinese_websites = [
    'https://www.taobao.com',
    'https://www.tmall.com',
    'https://www.jd.com',
]

# 使用线程池
with ThreadPoolExecutor(max_workers=3) as executor:
    for url in chinese_websites:
        executor.submit(open_url, browser, url)

线程池技术ThreadPoolExecutor 提供了线程池功能,可并发执行多个任务

并发打开网站:将 browser.new_tab(url) 方法提交到线程池执行

参数说明

  • max_workers=3:最多同时运行 3 个线程
  • executor.submit(func, *args):异步提交任务到线程池。
  •  *args即函数需要传入的参数

关键技术点

  1. 标签页管理

    • 通过 new_tab() 创建新标签页
    • 使用 latest_tab 获取最新标签页
    • 标签页对象可直接操作(如 tab.titletab.close()
  2. 并发控制

    • 线程池适合 I/O 密集型任务(如网页加载)
    • 控制并发数量可避免资源耗尽
    • DrissionPage 的标签页操作是线程安全的
  3. 执行流程

    • 主线程负责初始化和协调
    • 工作线程负责打开标签页
    • 所有操作共享同一个浏览器实例

3.2 元素定位之普通定位

from DrissionPage import Chromium

browser = Chromium()

tab = browser.new_tab('https://www.baidu.com')

# wenku_button = tab.ele('文库')
# wenku_button.click()

input_box = tab.ele('@id=kw')
input_box.input('123')

search_button = tab.ele('tag:input@@type=submit@@id=su@@value=百度一下')  #最精确
search_button.click()

 代码作用:定位到输入框位置,输入123,再点击搜索按钮

代码解释:

1. 文本定位 ('文库')

# 注释掉的代码:通过文本内容定位元素
# wenku_button = tab.ele('文库')
# wenku_button.click()

语法ele('文本内容') 或 ele('t:文本内容')

作用:查找包含指定文本的元素(默认优先匹配按钮、链接)

示例'文库' 会匹配文本为 "文库" 的按钮或链接

注意:此代码被注释,可能因页面结构变化导致定位失败

2. 属性定位 ('@id=kw')

input_box = tab.ele('@id=kw')  # 定位百度搜索框
input_box.input('123')

语法ele('@属性名=属性值')

作用:通过元素的属性值定位

示例

  • @id=kw:匹配 id="kw" 的元素(百度搜索框)
  • @name=username:匹配 name="username" 的元素

3. 复合定位 ('tag:input@@type=submit@@id=su@@value=百度一下')

search_button = tab.ele('tag:input@@type=submit@@id=su@@value=百度一下')
search_button.click()

语法:多条件组合,用 @@ 分隔

支持的条件类型

tag:标签名:按标签类型过滤(如 tag:input

@属性名=值:按属性值过滤(如 @id=su

text:文本内容:按文本内容过滤(如 text:百度一下

示例拆解

tag:input         # 标签为 input
@@type=submit     # 且 type 属性为 submit
@@id=su           # 且 id 属性为 su
@@value=百度一下  # 且 value 属性为 "百度一下"

定位逻辑

  1. 先筛选所有 <input> 标签
  2. 在结果中筛选 type="submit" 的元素
  3. 继续筛选 id="su" 的元素
  4. 最终筛选 value="百度一下" 的元素

DrissionPage 定位方法总结

定位方式语法示例说明
文本定位'文本内容' 或 't:文本内容'匹配包含指定文本的元素
属性定位'@属性名=属性值'通过属性值定位
标签定位'tag:标签名'通过标签类型定位
CSS 选择器'css:选择器内容'标准 CSS 选择器语法
XPath'xpath:表达式'标准 XPath 语法
复合定位'条件1@@条件2@@条件3'多条件组合定位

3.3 元素定位之iframe定位

from DrissionPage import Chromium
#只要是同域名的,无论跨多少层<iframe>都能用页面对象直接获取。
tab = Chromium().latest_tab
tab.get('https://DrissionPage.cn/demos/iframe_diff_domain.html')
# iframe = tab.get_frame('t:iframe')  #最规范
# ele = iframe.ele('网易首页')
# print(ele)


iframe = tab.ele('t:iframe')  #最规范
ele = iframe('网易首页')
print(ele)

 

代码功能概述

这段代码的主要目的是:

  1. 打开包含跨域 iframe 的演示页面
  2. 定位 iframe 元素
  3. 在 iframe 内部查找文本为 "网易首页" 的元素

代码详细解读

1. 导入模块并初始化浏览器

from DrissionPage import Chromium
tab = Chromium().latest_tab

创建 Chromium 浏览器实例并获取最新标签页

等价于以下写法:

browser = Chromium()
tab = browser.latest_tab

2. 访问目标页面

tab.get('https://DrissionPage.cn/demos/iframe_diff_domain.html')

打开演示页面,该页面包含至少一个跨域 iframe

跨域 iframe 指源域名与主页面不同的 iframe(如主页面是 .cn,iframe 是 .com

3. 定位 iframe 元素(原注释代码)

# iframe = tab.get_frame('t:iframe')  # 最规范
# ele = iframe.ele('网易首页')

get_frame() 方法:专门用于定位 iframe

  • 参数 't:iframe' 表示通过文本定位 iframe 元素
  • 定位成功后返回一个 Frame 对象

iframe.ele('网易首页'):在 iframe 内部查找文本为 "网易首页" 的元素

4. 实际使用的定位方法

iframe = tab.ele('t:iframe')  # 定位 iframe 元素
ele = iframe('网易首页')  # 在 iframe 内部查找元素
print(ele)

tab.ele('t:iframe')

  • 使用 ele() 方法直接定位 iframe 元素
  • 't:iframe' 表示通过文本内容定位(这里文本可能是 iframe 的 title 属性或内部文本)

iframe('网易首页')

  • 在已定位的 iframe 元素上下文中查找文本为 "网易首页" 的元素
  • 等价于 iframe.ele('网易首页')

关键技术点

  1. iframe 定位方式

    • get_frame() 方法:返回 Frame 对象,专门用于操作 iframe
    • ele() 方法:返回 Element 对象,可通过该对象访问 iframe 内部元素
  2. 跨域处理

    • DrissionPage 声称 "同域名的 iframe 可直接获取"
    • 但示例中的页面是跨域的(网易域名与演示页面不同)
    • 此处能成功定位可能是因为:
      • 演示页面使用了特殊技术允许跨域访问
      • DrissionPage 内部处理了跨域限制
  3. 元素定位语法

    • 't:文本内容' 是 DrissionPage 的简化定位语法
    • 等价于 'text:文本内容'

 iframe 基础概念

iframe 是 HTML 中用于在当前页面嵌入其他网页的标签,例如:

<iframe src="https://example.com"></iframe>

该标签会在当前页面中创建一个独立的窗口,显示 example.com 的内容。

 跨域的定义

当 iframe 的 src 域名、端口或协议与当前页面不同时,即为跨域 iframe。例如:

当前页面:https://www.baidu.com

iframe 源:https://news.baidu.com(同域名,不算跨域)

iframe 源:https://www.google.com(域名不同,跨域)

iframe 源:http://www.baidu.com(协议不同,跨域)

iframe 源:https://www.baidu.com:8080(端口不同,跨域)

3.4 数据监听和抓包

from DrissionPage import Chromium,ChromiumOptions
from pprint import pprint

options = ChromiumOptions()
#连接浏览器并获取浏览器对象
browser = Chromium(options)

tab = browser.latest_tab
tab.listen.start('spa1.scrape.center/api/movie')  #开始监听,指定获取包含该文本的数据包,注意是先开启监听再打开对应界面

tab.get('https://spa1.scrape.center/')   #访问网址,这行产生的数据包不抓取

for packet in tab.listen.steps():
    pprint(packet.response.body)  #打印数据包url




# tab = Chromium().latest_tab
# tab.get('https://gitee.com/explore/all')  # 访问网址,这行产生的数据包不监听
#
# tab.listen.start('gitee.com/explore')  # 开始监听,指定获取包含该文本的数据包
# for _ in range(5):
#     tab('@rel=next').click()  # 点击下一页
#     res = tab.listen.wait()  # 等待并获取一个数据包
#     print(res.url)  # 打印数据包url

 获取电影信息和网站

代码详细解读

1. 导入模块并配置浏览器选项

from DrissionPage import Chromium, ChromiumOptions
from pprint import pprint

options = ChromiumOptions()
browser = Chromium(options)

ChromiumOptions:用于配置浏览器启动参数(如无头模式、代理等)

Chromium:创建浏览器实例,支持 Chrome 或 Edge

2. 开始监听网络请求

tab = browser.latest_tab
tab.listen.start('spa1.scrape.center/api/movie')
  • tab.listen.start():开始监听网络请求
  • 参数 'spa1.scrape.center/api/movie':指定要捕获的请求 URL 包含的文本
    • 只要请求 URL 中包含该字符串,就会被捕获
    • 例如:https://spa1.scrape.center/api/movie/1 会被捕获

3. 访问目标网站

tab.get('https://spa1.scrape.center/')

打开目标网站,但此行代码执行前已启动监听

注意:此行代码产生的数据包不会被捕获(因为监听在访问前已启动)

4. 处理捕获的数据包

for packet in tab.listen.steps():
    pprint(packet.response.body)

tab.listen.steps():迭代获取所有捕获的数据包

packet.response.body:获取响应的内容(通常是 JSON 数据)

pprint():美化打印 JSON 数据,使其更易读

关键技术点

  1. 网络请求监听机制

    • DrissionPage 通过注入浏览器扩展或拦截器实现请求监听
    • 可监听的内容包括:请求 URL、请求头、响应内容等
  2. 数据包结构

    • packet.request:包含请求信息(URL、方法、头信息等)
    • packet.response:包含响应信息(状态码、内容、头信息等)
    • packet.response.body:响应的主体内容(如 JSON 数据)
  3. 监听时机

    • 必须在产生请求前调用 listen.start()
    • 示例中先监听再访问网站,确保所有请求被捕获

潜在问题与改进建议

  1. 数据包丢失风险

    • 如果在 listen.start() 前已经有请求发生,这些请求将不会被捕获
    • 改进:在创建浏览器实例后立即启动监听
  2. 过滤条件优化

    • 当前过滤条件 'spa1.scrape.center/api/movie' 可能匹配过多请求
    • 改进:使用更精确的匹配模式(如正则表达式)
  3. 异步处理

    • 对于大量数据包,可使用异步方式处理以提高性能
    • 改进:结合 asyncio 和 AsyncChromium
  4. 数据解析

    • 直接打印 body 可能输出二进制或乱码
    • 改进:添加 JSON 解析逻辑:
      import json
      try:
          data = json.loads(packet.response.body)
          pprint(data)
      except json.JSONDecodeError:
          print("非 JSON 数据:", packet.response.body)

3.5 多线程操作标签页

# from DrissionPage import SessionPage
# from pprint import pprint
# page = SessionPage()
# page.get('https://www.yznnw.com/txt/1655.html')
# eles_list = page('xpath:/html/body/div[2]/div[1]/div[4]/ul').eles('t:a')
# print(eles_list.get.texts())  # 获取所有元素的文本
# chapter_dict = {}
# for i in eles_list:
#     print(i.tag,i.text,i.attr('href'))
#     chapter_dict[i.text] = i.attr('href')
# pprint(chapter_dict)
# list_chapter = list(chapter_dict.keys())

from DrissionPage import Chromium,ChromiumOptions
from pprint import pprint
import threading
import concurrent.futures
#设置浏览器选项,使用系统用户路径
# co = ChromiumOptions().use_system_user_path()

#连接浏览器并获取对象
browser = Chromium()

#获取标签页对象并打开目标网址
tab_instance = browser.new_tab('https://www.yznnw.com/txt/1655.html')

#Xpath定位章节列表的无序列表
chapter_dict = {}

#从网页提取标题和链接
#tab_instance('xpath:/html/body/div[2]/div[1]/div[4]/ul')
#tab_instance(#id or '@class=section-list')
for chapter in tab_instance('xpath:/html/body/div[3]/div[2]/div/ul').eles('t:a'):
    chapter_dict[chapter.text] = chapter.attr('href')

#打印提取到的章节字典
pprint(chapter_dict)
list_chapter = list(chapter_dict.keys())

def fetch_and_save(chapter_title,chapter_url):
    new_tab = browser.new_tab(chapter_url)  #打开章节链接
    chapter_content = new_tab('@class=wp ov').text
    print(chapter_content)

    print(f'正在下载{chapter_title}')
    tab_instance.wait(1)

    #保存
    with open(chapter_title+'.txt','w',encoding='utf-8') as f:
        f.write(chapter_content)
    threading.Thread(target=new_tab.close).start() #关闭新标签页
    #new_tab.close()   #单线程
#单线程
# for a in list_chapter:
#     fetch_and_save(a,chapter_dict[a])

#多线程
def main():
    #创建一个线程池,最多允许4个线程同时运行
    with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
        #提交任务到线程池
        futures = [executor.submit(fetch_and_save,chapter_title,chapter_url)
                   for chapter_title,chapter_url in chapter_dict.items()]
if __name__ == '__main__':
    main()

多线程下载小说

代码功能概述

这个爬虫程序主要完成以下工作:

  1. 打开小说目录页,提取所有章节标题和链接
  2. 使用线程池并发下载多个章节内容
  3. 将每个章节内容保存为单独的文本文件
  4. 自动管理标签页,下载完成后关闭

代码详细解读

1. 导入模块与初始化浏览器

from DrissionPage import Chromium, ChromiumOptions
from pprint import pprint
import threading
import concurrent.futures

browser = Chromium()  # 初始化 Chromium 浏览器
tab_instance = browser.new_tab('https://www.yznnw.com/txt/1655.html')  # 打开小说目录页

2. 提取章节信息

chapter_dict = {}
for chapter in tab_instance('xpath:/html/body/div[3]/div[2]/div/ul').eles('t:a'):
    chapter_dict[chapter.text] = chapter.attr('href')  # 提取章节标题和链接

定位逻辑

  • 使用 XPath 定位章节列表的 <ul> 元素
  • 通过 eles('t:a') 提取所有章节链接(a 标签)
  • 将章节标题和链接存入字典 chapter_dict

3. 单线程下载实现(注释部分)

# 单线程下载
# for a in list_chapter:
#     fetch_and_save(a, chapter_dict[a])

4. 多线程下载实现

def fetch_and_save(chapter_title, chapter_url):
    new_tab = browser.new_tab(chapter_url)  # 打开新标签页
    chapter_content = new_tab('@class=wp ov').text  # 提取章节内容
    print(f'正在下载{chapter_title}')
    
    # 保存为文本文件
    with open(chapter_title + '.txt', 'w', encoding='utf-8') as f:
        f.write(chapter_content)
    
    # 在新线程中关闭标签页
    threading.Thread(target=new_tab.close).start()  

def main():
    with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
        futures = [executor.submit(fetch_and_save, title, url) 
                  for title, url in chapter_dict.items()]

核心逻辑

  • 线程池:使用 ThreadPoolExecutor 创建 4 个工作线程
  • 并发下载:每个线程负责打开一个章节页面,提取内容并保存
  • 标签页管理:下载完成后在后台线程中关闭标签页

关键技术点

  1. 元素定位方式

    • '@class=wp ov':通过类名定位元素
    • 't:a':通过文本定位链接元素
    • 结合 XPath 和 DrissionPage 简化语法
  2. 多线程设计

    • 线程池实现并发下载,提高效率
    • 每个线程操作独立标签页,避免干扰
    • 使用守护线程关闭标签页,避免阻塞主线程
  3. 文件保存

    • 使用章节标题作为文件名
    • 指定 utf-8 编码避免中文乱码

潜在问题与改进建议

  1. 路径处理

    • 章节标题可能包含非法文件名的字符(如 /* 等)
    • 改进:添加文件名过滤:
      import re
      safe_title = re.sub(r'[\\/:*?"<>|]', '_', chapter_title)
      
  2. 异常处理

    • 当前代码没有处理网络异常或页面加载失败
    • 改进:添加重试机制和异常捕获:
      from tenacity import retry, stop_after_attempt, wait_fixed
      
      @retry(stop=stop_after_attempt(3), wait=wait_fixed(2))
      def fetch_and_save(...):
          # 函数内容
      
  3. 性能优化

    • 线程数量可能不是最优,建议根据网络情况调整
    • 改进:动态调整线程数:
      max_workers = min(10, len(chapter_dict))  # 根据章节数量调整
      
  4. 进度监控

    • 无法直观了解整体下载进度
    • 改进:使用 tqdm 显示进度条:
      from tqdm import tqdm
      
      with ThreadPoolExecutor(max_workers=4) as executor:
          list(tqdm(executor.map(lambda args: fetch_and_save(*args), 
                           chapter_dict.items()), total=len(chapter_dict)))

3.6 异步操作浏览器标签页

import asyncio   #异步库
from DrissionPage import Chromium
from DataRecorder import Recorder
from loguru import logger   #日志库

#定义异步采集方法
async def collect(tab,recoder,title,page=1):
    #遍历所有标题元素
    for i in tab.eles('.title project-namespace-path'):
        # 获取某页所有库名称,记录到记录器
        recoder.add_data((title,i.text,page))
        logger.info((title,i.text,page))
    #获取下一页按钮
    btn = tab('@rel=next',timeout=2)
    #如果有下一页,点击翻页并递归调用自身
    if btn:
        btn.click(by_js=True)
        await asyncio.sleep(1)
        #增加页数并递归调用
        await collect(tab,recoder,title,page+1)

#主函数
async def main():
    #新建浏览器对象
    browser = Chromium()

    #第一页标签页访问网址
    tab1 = browser.new_tab('https://gitee.com/explore/ai')
    #新建一个标签页并访问一个网址,返回其对象
    tab2 = browser.new_tab('https://gitee.com/explore/machine-learning')

    #新建记录器对象
    recoder = Recorder('data.csv')

    #创建异步任务
    task1 = asyncio.create_task(collect(tab1,recoder,'ai'))   #collect定义的函数
    task2 = asyncio.create_task(collect(tab2,recoder,'机器学习'))

    #等待任务完成
    await task1
    await task2

    print('所有任务完成')
    recoder.record()

if __name__ == '__main__':
    asyncio.run(main())

        这段代码使用 异步编程 和 DrissionPage 框架实现了一个从 Gitee 网站采集项目信息的爬虫程序。它通过创建多个异步任务同时处理不同分类的项目列表,并将结果保存到 CSV 文件中。

代码功能概述

该程序主要完成以下工作:

  1. 异步采集:同时抓取 Gitee 上 "AI" 和 "机器学习" 分类的项目信息
  2. 自动翻页:递归处理每个分类下的所有页面
  3. 数据记录:使用 DataRecorder 将项目名称保存到 CSV 文件
  4. 日志管理:使用 loguru 记录采集过程

代码详细解读

1. 导入模块与工具

import asyncio
from DrissionPage import Chromium
from DataRecorder import Recorder
from loguru import logger

asyncio:Python 内置的异步编程库

Chromium:DrissionPage 的浏览器驱动类

Recorder:自定义数据记录器,用于保存 CSV 文件

logger:日志工具,提供比 print 更强大的日志功能

2. 定义异步采集函数

async def collect(tab, recoder, title, page=1):
    # 遍历当前页所有项目标题元素
    for i in tab.eles('.title project-namespace-path'):
        recoder.add_data((title, i.text, page))  # 记录数据
        logger.info((title, i.text, page))        # 记录日志

    # 查找下一页按钮
    btn = tab('@rel=next', timeout=2)
    if btn:
        btn.click(by_js=True)  # 使用 JavaScript 点击
        await asyncio.sleep(1)  # 等待页面加载
        await collect(tab, recoder, title, page+1)  # 递归处理下一页

元素定位:使用 CSS 选择器 .title project-namespace-path 定位项目标题

递归翻页:通过检查 rel="next" 的按钮实现自动翻页

异步等待:使用 asyncio.sleep(1) 等待页面加载,避免阻塞其他任务

3. 主函数与异步任务

async def main():
    browser = Chromium()  # 创建浏览器实例

    # 打开两个分类页面
    tab1 = browser.new_tab('https://gitee.com/explore/ai')
    tab2 = browser.new_tab('https://gitee.com/explore/machine-learning')

    recoder = Recorder('data.csv')  # 创建数据记录器

    # 创建并启动异步任务
    task1 = asyncio.create_task(collect(tab1, recoder, 'ai'))
    task2 = asyncio.create_task(collect(tab2, recoder, '机器学习'))

    # 等待所有任务完成
    await task1
    await task2

    print('所有任务完成')
    recoder.record()  # 将数据写入 CSV 文件

多标签页操作:在同一个浏览器实例中打开多个标签页

任务并行:使用 asyncio.create_task() 同时执行多个采集任务

数据持久化:通过 Recorder 将采集的数据保存到文件

关键技术点

  1. 异步爬虫设计

    • 使用 async def 定义异步函数
    • 通过 asyncio.create_task() 创建并行任务
    • 使用 await 等待 IO 操作,释放 CPU 资源
  2. 浏览器自动化

    • DrissionPage 的 tab.eles() 方法定位多个元素
    • btn.click(by_js=True):使用 JavaScript 执行点击,避免元素遮挡问题
  3. 数据记录与日志

    • Recorder 类封装数据收集和 CSV 写入逻辑
    • logger.info() 提供结构化日志,支持时间戳、级别等信息

潜在问题与改进建议

  1. CSS 选择器问题

    • .title project-namespace-path 可能无法正确定位元素(可能应为 .title.project-namespace-path
    • 改进:使用浏览器开发者工具验证选择器
  2. 递归深度限制

    • 递归翻页可能导致 RecursionError(默认递归深度约 1000)
    • 改进:改用迭代方式实现翻页:
      while True:
          # 处理当前页
          btn = tab('@rel=next', timeout=2)
          if not btn:
              break
          btn.click(by_js=True)
          await asyncio.sleep(1)
      
  3. 异常处理

    • 缺少网络异常、元素定位失败等处理
    • 改进:添加 try-except 块:
      async def collect(...):
          try:
              # 原有代码
          except Exception as e:
              logger.error(f"采集失败: {e}")
              return
      
  4. 性能优化

    • 单浏览器实例多标签页可能不稳定
    • 改进:为每个任务创建独立浏览器实例:
      async def worker(url, category):
          browser = Chromium()
          tab = browser.new_tab(url)
          # 执行采集
          browser.quit()  # 关闭浏览器
      
      # 在 main 函数中创建多个 worker 任务

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寄思~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值