MediaCrawler,轻松爬取xhs图片和评论

前言

大家好,我是海鸽。今天我要介绍一款功能强大的社交媒体内容抓取工具。

它能够一键抓取包括xhs、某音、某手、B站、以及微博在内的多个平台上的图片、视频、评论、点赞、转发等信息,让您轻松获取所需内容。

这个项目开源不到一年,目前在Github上拥有15k+ stars5k+ forks,曾经更是登上Github Trending

因为热度过高,作者担心某些很的原因,还曾经删库。不过还是被某些有心之人偷了代码,在网上卖钱用于商业行为。因此作者补充了免责声明,重新开源,并提醒大家不要上当受骗。

项目地址:github.com/NanmiCoder/...


项目简介

MediaCrawler,由NanmiCoder精心打造,是一个在Github上广受好评的开源项目。它专注于高效地抓取小红书、抖音、快手、B站和微博等热门社交平台的视频与帖子评论,开箱即用。

MediaCrawler提供定制化数据抓取,配备IP代理池以规避封锁风险,并支持多种数据格式保存,满足多样化的数据处理需求。

采用Python作为核心开发语言,原生支持并发,MediaCrawler不仅实现了强大的数据抓取功能,更以其开放的源代码,方便实现二次开发,为广大开发者和学习者提供了一个学习和探索网络爬虫技术的理想平台。

功能列表

平台 关键词搜索 指定帖子ID爬取 二级评论 指定创作者主页 登录态缓存 IP代理池 生成评论词云图
小红书
抖音
快手
B 站
微博
贴吧

这个项目如何使用呢

MediaCrawler作为一个Python项目,其使用前提自然是确保您的系统已安装了Python环境。

至于如何使用MediaCrawler,作者已经非常贴心地在项目的README文件中提供了详尽的指导。只需按照README中的步骤操作,即使是初学者也能轻松上手。简而言之,您需要做的只是阅读README文件,然后跟随其中的指引,即可开始您的爬虫之旅。

下载/克隆项目到本地

shell 复制代码
git clone https://github.com/NanmiCoder/MediaCrawler.git

创建并激活 python 虚拟环境

shell 复制代码
# 进入项目根目录
cd MediaCrawler

# 创建虚拟环境
# 注意python 版本需要3.7 - 3.9 高于该版本可能会出现一些依赖包兼容问题
python -m venv venv

# macos & linux 激活虚拟环境
source venv/bin/activate

# windows 激活虚拟环境
venv\Scripts\activate

安装依赖库

shell 复制代码
pip install -r requirements.txt

安装 playwright 浏览器驱动

shell 复制代码
playwright install

数据保存

MediaCrawler的数据存储模块经过精心封装,简化了大量数据的存储过程。源代码完全开放,便于深入研究和定制,可以在config/base_config.py进行配置。

  • 支持保存到关系型数据库(MysqlPgSQL等)
    • 执行 python db.py 初始化数据库数据库表结构(只在首次执行)
  • 支持保存到csv中(data/目录下
  • 支持保存到json中(data/目录下

运行爬虫程序

shell 复制代码
### 项目默认是没有开启评论爬取模式,如需评论请在config/base_config.py中的 ENABLE_GET_COMMENTS 变量修改
### 一些其他支持项,也可以在config/base_config.py查看功能,写的有中文注释

# 从配置文件中读取关键词搜索相关的帖子并爬取帖子信息与评论
python main.py --platform xhs --lt qrcode --type search

# 从配置文件中读取指定的帖子ID列表获取指定帖子的信息与评论信息
python main.py --platform xhs --lt qrcode --type detail

# 打开对应APP扫二维码登录
  
# 其他平台爬虫使用示例,执行下面的命令查看
python main.py --help    

xhs(二维码登录)关键字搜索为例,当我们执行启动命令后,MediaCrawler将为我们拉起无头浏览器,并弹出小红书二维码。

MediaCrawler提供了3种登录小红书的方式:QRCode手机号Cookie,登录相关代码都在media_platform/xhs/login.py文件中。

我们扫码登录后,MediaCrawler将通过无头浏览器技术识别并保存您的身份信息。这样一来,我们就可以在后续的会话中,以该身份为基础,安全地抓取所需的数据(我胆小,我选择用小号)。

数据就被这么简单地抓取下来了。

项目架构与代码结构

原理 :利用playwright搭桥,保留登录成功后的上下文浏览器环境,通过执行JS表达式获取一些加密参数。通过使用此方式,免去了复现核心加密JS代码,逆向难度大大降低。

MediaCrawler是一款开箱即用的Python项目,它采用Python的原生协程模式进行开发,具备清晰的分层架构,这种设计不仅简化了代码的编写,而且极大地提高了代码的可读性和可维护性。这种模式为二次开发提供了极大的便利,同时也为实现高并发的爬虫模型打下了坚实的基础。

shell 复制代码
# 源自官网,未更新微博、贴吧目录,仅供参考
MediaCrawler
├── base 
│   └── base_crawler.py         # 项目的抽象类
├── browser_data                # 换成用户的浏览器数据目录 
├── config 
│   ├── account_config.py       # 账号代理池配置
│   ├── base_config.py          # 基础配置
│   └── db_config.py            # 数据库配置
├── data                        # 数据保存目录  
├── libs 
│   ├── douyin.js               # 抖音Sign函数
│   └── stealth.min.js          # 去除浏览器自动化特征的JS
├── media_platform
│   ├── douyin                  # 抖音crawler实现
│   ├── xhs                     # 小红书crawler实现
│   ├── bilibili                # B站crawler实现 
│   └── kuaishou                # 快手crawler实现
├── modles 
│   ├── douyin.py               # 抖音数据模型
│   ├── xiaohongshu.py          # 小红书数据模型
│   ├── kuaishou.py             # 快手数据模型
│   └── bilibili.py             # B站数据模型 
├── tools
│   ├── utils.py                # 暴露给外部的工具函数
│   ├── crawler_util.py         # 爬虫相关的工具函数
│   ├── slider_util.py          # 滑块相关的工具函数
│   ├── time_util.py            # 时间相关的工具函数
│   ├── easing.py               # 模拟滑动轨迹相关的函数
|   └── words.py				# 生成词云图相关的函数
├── db.py                       # DB ORM
├── main.py                     # 程序入口
├── var.py                      # 上下文变量定义
└── recv_sms_notification.py    # 短信转发器的HTTP SERVER接口

此外,MediaCrawler的解决方案同样适用于那些具有较高逆向工程难度的网站。它能够提供有效的策略来规避这些挑战,确保数据抓取的效率和稳定性。

核心原理(以xhs为例)

xhs相关逻辑位于以下几个文件中:

xhs的代码并不是很多,主要逻辑在core.pyclient.pylogin.pyhelp.py中。

Sign签名算法

小红书浏览器端接口有做sign验签media_platform/xhs/client.py文件中的_pre_headers方法生成sign相关参数。

python 复制代码
async def _pre_headers(self, url: str, data=None) -> Dict:
    """
    请求头参数签名
    Args:
        url:
        data:
    Returns:
    """
    encrypt_params = await self.playwright_page.evaluate("([url, data]) => window._webmsxyw(url,data)", [url, data])
    local_storage = await self.playwright_page.evaluate("() => window.localStorage")
    signs = sign(
        a1=self.cookie_dict.get("a1", ""),
        b1=local_storage.get("b1", ""),
        x_s=encrypt_params.get("X-s", ""),
        x_t=str(encrypt_params.get("X-t", ""))
    )

    headers = {
        "X-S": signs["x-s"],
        "X-T": signs["x-t"],
        "x-S-Common": signs["x-s-common"],
        "X-B3-Traceid": signs["x-b3-traceid"]
    }
    self.headers.update(headers)
    return self.headers

主要签名逻辑sign函数则是位于media_platform/xhs/help.py文件中。

python 复制代码
def sign(a1="", b1="", x_s="", x_t=""):
    """
    takes in a URI (uniform resource identifier), an optional data dictionary, and an optional ctime parameter. It returns a dictionary containing two keys: "x-s" and "x-t".
    """
    common = {
        "s0": 3,  # getPlatformCode
        "s1": "",
        "x0": "1",  # localStorage.getItem("b1b1")
        "x1": "3.7.8-2",  # version
        "x2": "Mac OS",
        "x3": "xhs-pc-web",
        "x4": "4.27.2",
        "x5": a1,  # cookie of a1
        "x6": x_t,
        "x7": x_s,
        "x8": b1,  # localStorage.getItem("b1")
        "x9": mrc(x_t + x_s + b1),
        "x10": 154,  # getSigCount
    }
    encode_str = encodeUtf8(json.dumps(common, separators=(',', ':')))
    x_s_common = b64Encode(encode_str)
    x_b3_traceid = get_b3_trace_id()
    return {
        "x-s": x_s,
        "x-t": x_t,
        "x-s-common": x_s_common,
        "x-b3-traceid": x_b3_traceid
    }

反反爬虫

python 复制代码
async def start(self) -> None:
    playwright_proxy_format, httpx_proxy_format = None, None
    if config.ENABLE_IP_PROXY:
        ip_proxy_pool = await create_ip_pool(config.IP_PROXY_POOL_COUNT, enable_validate_ip=True)
        ip_proxy_info: IpInfoModel = await ip_proxy_pool.get_proxy()
        playwright_proxy_format, httpx_proxy_format = self.format_proxy_info(ip_proxy_info)

    async with async_playwright() as playwright:
        # Launch a browser context.
        chromium = playwright.chromium
        self.browser_context = await self.launch_browser(
            chromium,
            None,
            self.user_agent,
            headless=config.HEADLESS
        )
        # stealth.min.js is a js script to prevent the website from detecting the crawler.
        await self.browser_context.add_init_script(path="libs/stealth.min.js")
        # add a cookie attribute webId to avoid the appearance of a sliding captcha on the webpage
        await self.browser_context.add_cookies([{
            'name': "webId",
            'value': "xxx123",  # any value
            'domain': ".xiaohongshu.com",
            'path': "/"
        }])
        self.context_page = await self.browser_context.new_page()
        await self.context_page.goto(self.index_url)

        # Create a client to interact with the xiaohongshu website.
        self.xhs_client = await self.create_xhs_client(httpx_proxy_format)
        if not await self.xhs_client.pong():
            login_obj = XiaoHongShuLogin(
                login_type=config.LOGIN_TYPE,
                login_phone="",  # input your phone number
                browser_context=self.browser_context,
                context_page=self.context_page,
                cookie_str=config.COOKIES
            )
            await login_obj.begin()
            await self.xhs_client.update_cookies(browser_context=self.browser_context)

        crawler_type_var.set(config.CRAWLER_TYPE)
        if config.CRAWLER_TYPE == "search":
            # Search for notes and retrieve their comment information.
            await self.search()
        elif config.CRAWLER_TYPE == "detail":
            # Get the information and comments of the specified post
            await self.get_specified_notes()
        elif config.CRAWLER_TYPE == "creator":
            # Get creator's information and their notes and comments
            await self.get_creators_and_notes()
        else:
            pass

        utils.logger.info("[XiaoHongShuCrawler.start] Xhs Crawler finished ...")

可以看到作者在media_platform/xhs/core.pystart函数实现了诸如以下的反反爬策略:

  • 支持设置IP代理
  • 反headless浏览器检测
  • 预置cookies防止出现滑块验证码
  • 登录状态确认

小结

总体而言,MediaCrawler不仅是一个值得深入研究的项目,更是一个卓越的学习典范。它不仅为我们提供了一个全新的视角来深入理解和探索爬虫技术,还为我们在爬虫领域的学习和实践提供了宝贵的参考和启发。

希望大家在学习的过程中,能够遵守相关法规法条,合理合法使用爬虫技术。

最后

今天的分享就到这里。如果觉得不错,点赞关注安排起来吧。

相关推荐
bugtraq202111 小时前
闲鱼网页版开放,爬虫的难度指数级降低。
爬虫
Bigcrab__16 小时前
Python3网络爬虫开发实战(15)Scrapy 框架的使用(第一版)
爬虫·python·scrapy
九月镇灵将16 小时前
爬虫逆向学习(六):补环境过某数四代
爬虫·补环境·瑞数
kngines17 小时前
【PLW004】基于Python网络爬虫与推荐算法的新闻推荐平台v1.0(Python+Django+NLP+Vue+MySQL前后端分离)
爬虫·python·nlp·推荐算法
walk walk1 天前
免费爬虫软件“HyperlinkCollector超链采集器v0.1”
爬虫
亿牛云爬虫专家1 天前
如何通过subprocess在数据采集中执行外部命令 —以微博为例
爬虫·python·数据采集·多线程·代理ip·subprocess·微博
菜鸡中的奋斗鸡→挣扎鸡1 天前
初始爬虫5
爬虫
无敌开心1 天前
Django-Celery-Flower实现异步和定时爬虫及其监控邮件告警
爬虫·python·django