Python中Scrapy采集百度贴吧数据入库MySQL后,如何按发帖时间顺序排序入库?

scrapy 采集百度贴吧入库 mysql 后。入库数据时间是混乱的,有什么办法可以按百度贴吧发贴时间顺序入库不?

如图:

https://static.oschina.net/uploads/space/2017/0430/125704_3lK4_2628079.png


后面的这一列时间。是我采集的百度贴吧楼主发贴的时间。入库后发现这个好混乱。

如果想把百度贴吧发贴的时间顺序入库。

请问实现这个需要什么思路?
Python中Scrapy采集百度贴吧数据入库MySQL后,如何按发帖时间顺序排序入库?


25 回复

我的天,不能 order by 一下吗


问题核心: 在Scrapy中,采集的数据按顺序入库的关键在于在数据处理流程中尽早完成排序,而不是依赖数据库的默认插入顺序。

解决方案: 在数据到达pipeline进行数据库操作前,通过Scrapy的Item Pipeline或中间件对数据进行排序。最直接有效的方法是在close_spider时,对所有收集到的item进行一次集中排序并批量入库。

代码示例:

# pipelines.py
import pymysql
from datetime import datetime

class BaiduTiebaMySQLPipeline:
    def __init__(self, mysql_settings):
        self.mysql_settings = mysql_settings
        self.items_buffer = []  # 缓冲列表,用于存储所有item

    @classmethod
    def from_crawler(cls, crawler):
        return cls(
            mysql_settings=crawler.settings.get('MYSQL_SETTINGS')
        )

    def open_spider(self, spider):
        # 连接数据库
        self.conn = pymysql.connect(**self.mysql_settings)
        self.cursor = self.conn.cursor()

    def process_item(self, item, spider):
        # 将item暂存到缓冲列表,不立即入库
        self.items_buffer.append(item)
        return item

    def close_spider(self, spider):
        if not self.items_buffer:
            return

        # 1. 按发帖时间排序(假设字段为'post_time',且已是datetime对象或可解析的字符串)
        try:
            sorted_items = sorted(
                self.items_buffer,
                key=lambda x: self._parse_time(x['post_time'])
            )
        except KeyError:
            spider.logger.error("Item中未找到'post_time'字段")
            sorted_items = self.items_buffer

        # 2. 批量入库
        try:
            for item in sorted_items:
                # 构建SQL,这里假设你的表结构
                sql = """
                    INSERT INTO tieba_posts (title, author, post_time, content)
                    VALUES (%s, %s, %s, %s)
                """
                values = (
                    item['title'],
                    item['author'],
                    item['post_time'],  # 确保已经是正确的MySQL datetime格式
                    item['content']
                )
                self.cursor.execute(sql, values)
            self.conn.commit()
        except Exception as e:
            spider.logger.error(f"入库失败: {e}")
            self.conn.rollback()
        finally:
            self.cursor.close()
            self.conn.close()

    def _parse_time(self, time_str):
        """将时间字符串转换为datetime对象用于排序"""
        # 根据你实际的时间格式调整,例如:
        # 如果是"2023-10-01 12:30:00"格式
        return datetime.strptime(time_str, "%Y-%m-%d %H:%M:%S")
        # 如果是时间戳
        # return datetime.fromtimestamp(float(time_str))

# settings.py 中启用并配置
ITEM_PIPELINES = {
    'your_project.pipelines.BaiduTiebaMySQLPipeline': 300,
}

MYSQL_SETTINGS = {
    'host': 'localhost',
    'user': 'root',
    'password': 'your_password',
    'database': 'tieba_data',
    'charset': 'utf8mb4',
}

关键点说明:

  1. 缓冲机制process_item只收集数据到self.items_buffer列表,不立即写入数据库。
  2. 集中排序:在爬虫关闭时(close_spider),对所有缓冲的数据按post_time字段排序。
  3. 批量入库:按排序后的顺序依次执行INSERT操作。
  4. 时间处理:确保post_time字段格式统一,在_parse_time方法中根据你的实际时间格式进行解析。

如果你的数据量很大,担心内存问题,可以考虑:

  • 使用数据库临时表:先无序插入临时表,然后用ORDER BY查询后插入主表。
  • 分批缓冲:每收集N条item就排序入库一次。

一句话总结: 在Pipeline中缓冲所有数据,爬虫关闭前统一按时间排序后再批量入库。

为什么要保证数据库里是顺序的?

爬的时候把百度输出的 发帖 /回复 时间也爬了,然后按时间排序呗

insert into 数据库没法 order by 吧?

我是想实现这样的功能:
比如贴子时间分别是:2010 年 5 月 2011 年 5 月 2012 年 5 月 2013 年 5 月 2014 年 5 月 2015 年 5 月 的六个贴子。

现在用 scrapy 采集到 item 里入库 mysql 的时候。这个时间是混乱的。有可能 id 对应的时间是 2015 年的, 也有可能是 2013 年的。
而我想实现的功能是:id 1 对应的是最早的贴子 2010 年 5 月的 id 2 对应的次早的贴子 2011 年 5 月的 最大的 id 6 对应的是 2015 年的贴子,这样我调用出来的时候显示的才是最新的。

不知道这个是怎么排序的? 是入库前在 scrapy 的 item 里就排好了序? 还是在入库 mysql 的时候排? 还是都不行。只能在用代码调用数据的时候排数据? 而不管这个 id 是多少?

插入排序

因为如果那时间是混乱的。 第一条数据 id 1 可能对应的是最新的贴子,而最大的 id 也就是最后入库的那个贴子有可能是是几年前的最老的那个贴子。 这样很不好吧?

这样? INSERT INTO tbl_name (a1 , a2 , a3) VALUES (b1, b2, b3 ) order by b3 desc;
b3 是百度发贴的时间
这样吗? 好像不行。

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。为什么要保证 id 是按顺序的。你加个发帖时间字段不行吗。

加个发贴时间。那入库的时间就是新的发贴时间了。我是想保留原发贴时间。就用原发贴时间。最好原发贴时间与 id 对应。

你直接说你的根本需求,大家帮你出主意。你的解决方案完全是无意义的,没有讨论价值

感觉你完全不懂 sql。。。

那你别用自增 id 呗。

是的。新手正在边查资料边试验。只会最基本的几条语句


这样的需求。不知道这样描述,能表达清楚不:

我是想实现这样的功能:
比如贴子时间分别是:2010 年 5 月 2011 年 5 月 2012 年 5 月 2013 年 5 月 2014 年 5 月 2015 年 5 月 的六个贴子。

现在用 scrapy 采集到 item 里入库 mysql 的时候。这个时间是混乱的。有可能 id 对应的时间是 2015 年的, 也有可能是 2013 年的。
而我想实现的功能是:id 1 对应的是最早的贴子 2010 年 5 月的 id 2 对应的次早的贴子 2011 年 5 月的 最大的 id 6 对应的是 2015 年的贴子,这样我调用出来的时候显示的才是最新的。

不知道这个是怎么排序的? 是入库前在 scrapy 的 item 里就排好了序? 还是在入库 mysql 的时候排? 还是都不行。只能在用代码调用数据的时候排数据? 而不管这个 id 是多少?

“加个发贴时间。那入库的时间就是新的发贴时间了。我是想保留原发贴时间。就用原发贴时间。最好原发贴时间与 id 对应。”

搞不懂你这前半句什么意思,既然能爬到发帖时间,就把这个时间存到一个字段里,需要 select 的时候按这个字段排序不就完了

我明白楼主的意思,楼主采用的采集框架或者别人的采集代码把最后回复时间当成了帖子时间,这个很简单啊,自己想办法抓取到主帖的发帖时间就行了啊。
PS:我前不久也做了百度贴吧采集。

说真的,我也不会去关心入库的顺序,反正读的时候 order by

有个名词叫时间戳,具体含义可以去百度下,你抓到的 2017-1-24 10:31 这样的时间数据都可以很方便的转换为一个唯一的时间戳数字,把这个数字保存到一个字段,查询时候用这个数字排序即可

为啥要管插入顺序,select 的时候排个序不就好了
至于时间,直接把网页上的时间转成时间戳保存进数据库不就行了

首先看到你的时间前面带 u,说明你时间还是 str 类型的,其次百度贴吧 tid 帖子号是时间递增的,直接用正则表达式分割出 url 里面 p 后面的数字,然后 order by 一下就可以,另外客户端接口可以直接抓取到时间戳

我的 github.com/cw1997,里面有几个贴吧相关的爬虫项目,php 的 py 的都有,你可以找找看。zhuanlan.zhihu.com/codes 近期也会发布相关文章,欢迎持续关注。

原作的根本问题在于,情感上不愿意接受:sql 数据库本身不提供顺序,顺序必须用一个字段的排序来描述,这个事实
“现在真是什么人都能写爬虫了”

我猜,楼主是处女座~

你对数据的 id 有多执着?
你只是要显示的时候按照时间排序而已,这管 id 什么事?你直接进数据库把整个 id 列给砍了也不影响你的需求啊。
10 楼就是问你到底需求是什么,还在那拼命扯 id 的问题,你到底是要看帖子还是要看 id 啦?

1,id 只唯一标识一条数据,除此之外什么作用都不应该有,包括顺序。改天换成 UUID,还怎么保证有序?
2,数据插入的顺序,不代表 select 出的顺序。要想有序,必须 order by。
3,原帖时间就应该是原帖时间,既不是回帖时间也不是抓取时间。如果不存在,请自己抓出、创建字段,并依此排序。

回到顶部