DrissionPage实战案例:小红书旅游数据爬取

DrissionPage实战案例:小红书旅游数据爬取

目标

爬取小红书旅游相关内容数据,包括:

  • 笔记标题

  • 图片数量

  • 点赞数

  • 收藏数

  • 评论数

  • 详情页URL

  • 下载笔记中的所有图片

技术栈

  • DrissionPage:用于自动化浏览器操作和数据包监听

  • Requests:用于发送HTTP请求

  • 正则表达式:用于解析HTML内容

  • CSV模块:用于数据存储

代码

# 导入模块
import csv
​
from DrissionPage import ChromiumPage
import requests
import re
import time
import random
import os
​
​
# 创建xhs_data来存储数据
base_dir = 'xhs_data'
if not os.path.exists(base_dir):
    os.mkdir(base_dir)
​
# 创建页面
page = ChromiumPage()
# 获取标签页
tab = page.latest_tab
# 开始监听数据包
tab.listen.start('api/sns/web/v1/search/notes')
print('准备打开页面,等待页面加载完成')
# 打开页面
tab.get('https://www.xiaohongshu.com/search_result?keyword=%25E6%2597%2585%25E6%25B8%25B8&source=web_explore_feed')
# 等待页面加载完成
tab.wait.load_start()
"""获取数据"""
# 获取第一个数据包
res = tab.listen.wait()
# 获取响应的数据
dict_data = res.response.body
items_list = []
data_count = 1
try:
    while True:
        # 退出死循环条件
        if dict_data['data']['has_more'] == False:
            print('总共加载数据条数为', len(items_list))
            break
        else:
            items = dict_data['data']['items']
            items_list.append(items)
            print(f'第{data_count}个数据包获取完成')
            time.sleep(random.uniform(1, 3))
            data_count += 1
            # 第1次滚动
            tab.scroll.to_bottom()
            # 获取下一个数据包,并且等待时间为5秒
            res = tab.listen.wait(timeout=5)
            # 如果第一次滚动之后没有数据包加载,则继续进行滚动
            if not res:
                tab.run_js('window.scrollBy(0,2500)')
                # 获取数据包,并且时间设置为5秒
                res = tab.listen.wait(timeout=5)
                # 如果还没有数据包,那么就表示已经全部加载完毕了
                if not res:
                    print('数据可能到达底部')
            # 获取响应的数据
            dict_data = res.response.body
except Exception as e:
    print('获取数据包失败',e)
# 构造请求头
headers = {
    'user-angent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36',
    'cookie':'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
}
try:
    with open(os.path.join(base_dir,'xhs.csv'),'w',newline='',encoding='utf-8') as f:
        # 定义表头,并且写入
        fieldnames = ['标题', '图片数量', '点赞数', '评论数', '收藏数', '详情页url']
        writer = csv.DictWriter(f, fieldnames=fieldnames)
        writer.writeheader()
        """处理数据"""
        for items in items_list:
            try:
                for i,item in enumerate(items):
​
                    # 定义字典,用于打印结果,并且写入csv文件
                    my_data_dict = {
                        '标题': '',
                        '图片数量': '',
                        '点赞数': '',
                        '评论数': '',
                        '收藏数': '',
                        '详情页url': ''
                    }
​
                    # 获取发布笔记的id
                    note_id = item['id']
                    # 获取发布笔记的xsec_token
                    xsec_token = item['xsec_token']
                    # 分析详情页url地址
                    # https://www.xiaohongshu.com/explore/679e13ca000000002902c13b?xsec_token=AB2xkQBCe8dz6uVelB_qg4jrO1UjgHPHU64XBQ8Qd236w=&xsec_source=pc_search&source=web_explore_feed
                    # 发现679e13ca000000002902c13b是获取的id,AB2xkQBCe8dz6uVelB_qg4jrO1UjgHPHU64XBQ8Qd236w=&xsec_source=为获取的xsec_token
                    # 构造url
                    url = f'https://www.xiaohongshu.com/explore/{note_id}?xsec_token={xsec_token}&xsec_source=pc_search&source=web_explore_feed'
                    my_data_dict['详情页url'] = url
                    try:
                        # 随机1~3秒,发送请求
                        time.sleep(random.uniform(1, 3))
                        # 发送请求,获取响应
                        response = requests.get(url, headers=headers)
                        # 如果响应码为200
                        if response.status_code == 200:
                            html = response.text
                            # print(html)
                            # 提取title数据
                            title_match = re.search(r'<title>(.*?) - 小红书</title>',html,re.S)
                            if title_match:
                                old_title = title_match.group(1)
                                # 进行安全名的处理
                                title = re.sub(r'[\\/:*?"<>|\t\r\n]', '', old_title)[:50]
                                # 创建目录,用来存放图片
                                note_file = os.path.join(base_dir,title)
                                my_data_dict['标题'] = title
                                if not os.path.exists(note_file):
                                    os.makedirs(note_file)
                            else:
                                my_data_dict['标题'] = '无法获取'
                                print(f'第{i+1}篇笔记无法获取标题')
                            # 从HTML中提取所有的图片链接
                            image_urls = re.findall(r'<meta name="og:image" content="(.*?)">', html,re.S)
                            if image_urls and title:
                                print(f'获取到{len(image_urls)}张图片~')
                                my_data_dict['图片数量'] = len(image_urls)
                                # 再次发送请求,请求下载图片
                                for j,image_url in enumerate(image_urls):
                                    response_img_download = requests.get(image_url,headers=headers,timeout=20)
                                    # 如果状态码等于200,则执行以下代码
                                    if response_img_download.status_code == 200:
                                        if os.path.exists(note_file):
                                            # 如果文件夹存在,则写入
                                            # 写入文件
                                            try:
                                                with open(os.path.join(note_file, f'image_{j}.jpg'), 'wb') as f:
                                                    f.write(response_img_download.content)
                                                    print(f'已保存{j + 1}张图片')
                                            except Exception as e:
                                                print(f'保存第{j + 1}图片失败')
                                        # 非则先创建文件夹,再写入
                                        else:
                                            os.makedirs(note_file)
                                            try:
                                                with open(os.path.join(note_file, f'image_{j}.jpg'), 'wb') as f:
                                                    f.write(response_img_download.content)
                                                    print(f'已保存{j + 1}张图片')
                                            except Exception as e:
                                                print(f'保存第{j + 1}图片失败')
                                    else:
                                        print(f'请求第{j}张图片失败,无法获取响应')
                            else:
                                print(f'第{i+1}篇笔记无法获取详情页图片url,或者因为没有获取到标题,无法创建文件')
                                my_data_dict['图片数量'] = '无法获取'
​
​
                            # 获取点赞数
                            like = re.search(r'<meta name="og:xhs:note_like" content="(.*?)">',html,re.S)
                            if not like:
                                my_data_dict['点赞数'] = '无法获取'
                            else:
                                my_data_dict['点赞数'] = like.group(1)
                            # 获取收藏数
                            collect = re.search(r'<meta name="og:xhs:note_collect" content="(.*?)">',html,re.S)
                            if not collect:
                                my_data_dict['收藏数'] = '无法获取'
                            else:
                                my_data_dict['收藏数'] = collect.group(1)
                            # 获取评论数
                            comment = re.search(r'<meta name="og:xhs:note_comment" content="(.*?)">',html,re.S)
                            if not comment:
                                my_data_dict['评论数'] = '无法获取'
                            else:
                                my_data_dict['评论数'] = comment.group(1)
                            print(my_data_dict)
                            try:
                                writer.writerow(my_data_dict)
                            except Exception as e:
                                print('写入文件失败',e)
                        else:
                            print('没有成功获取响应')
                    except Exception as e:
                        print(f'没有成功获取响应,错误内容为:{e}')
​
            except Exception as e:
                print('处理数据发送错误,',e)
except Exception as e:
    print('打开文件失败',e)

### 什么是 DrissionPageDrissionPage 是一个基于 Python 的网页自动化工具,其核心优势在于将浏览器自动化与 HTTP 请求的功能结合在一起。它不仅能够像 Selenium 一样控制浏览器,还能像 requests 一样发送和接收数据包,从而兼顾动态页面处理的便利性和静态数据请求的高效性。 ### DrissionPage 的实际应用案例 #### 1. 网页自动化任务 DrissionPage 可以用于执行各种网页自动化任务,例如自动填写表单、点击按钮、提取网页数据等。以下是一个简单的示例代码,展示如何使用 DrissionPage 访问网页并提取数据: ```python from DrissionPage import DrissionPage # 初始化 DrissionPage 实例 dp = DrissionPage() # 启动浏览器并访问目标网页 dp.get('https://example.com') # 查找并点击某个按钮 button = dp.ele('tag:button@text=提交') button.click() # 提取网页中的数据 data = dp.ele('tag:div@class=data').text print(data) ``` #### 2. 数据爬取 DrissionPage 还可以用于爬取网页数据,尤其适合需要处理动态加载内容的网站。以下是一个简单的数据爬取示例: ```python from DrissionPage import DrissionPage # 初始化 DrissionPage 实例 dp = DrissionPage() # 访问目标网页 dp.get('https://example.com') # 获取网页中的所有链接 links = dp.eles('tag:a') for link in links: print(link.attr('href')) ``` #### 3. 自动化测试 DrissionPage 也可以用于编写自动化测试脚本,验证网页功能是否正常。以下是一个简单的测试示例: ```python from DrissionPage import DrissionPage # 初始化 DrissionPage 实例 dp = DrissionPage() # 启动浏览器并访问测试网页 dp.get('https://example.com') # 输入用户名和密码 username = dp.ele('id=username') password = dp.ele('id=password') username.input('test_user') password.input('test_password') # 点击登录按钮 login_button = dp.ele('id=login') login_button.click() # 验证登录是否成功 success_message = dp.ele('id=success').text assert success_message == '登录成功' ``` ### DrissionPage 的配置与启动 项目的启动通常是通过 `./_base` 目录下的入口文件进行的。这个文件负责初始化项目,设置配置信息,并启动浏览器会话。以下是一个示例启动文件内容: ```python from _base import DrissionPage # 初始化配置 config = { # ... 配置信息 ... } # 创建 DrissionPage 实例 dp = DrissionPage(config) # 启动浏览器 dp.start_browser() ``` ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值