使用Python+Scrapy爬取并保存QQ群空间帖子

2023-05-16

首先声明,在Python和爬虫这方面,我是业余的那一卦,只是平时玩一玩,不能当真的,请各位大佬轻拍。虽然爬虫与传统意义上的大数据技术不属于同一类,但大概也只能放在大数据分类下面了。
今天接到了 @小阿妩 的一个需求(她是做产品经理的,也只有“需求”这个词最合适了)。大意是因为担心QQ群空间不稳定或者关闭,因此需要备份某个QQ群空间的所有帖子。帖子量比较大,有几千条,人工操作会很麻烦,才会想到用爬虫来解决问题。
事不宜迟,下班之后马上用Scrapy来搞一波。由于日更的时间快到了,因此下面写得有点简略,之后再来慢慢充实内容吧。

安装Scrapy

我之前就已经装过了,按照官方文档http://doc.scrapy.org/en/latest/intro/install.html的介绍来,基本就可以万无一失。中途可能需要解决一下个别组件的依赖版本问题,比如six、Twisted、pyOpenSSL。另外我用的Python版本是2.7.10。
从官方的架构说明文档http://doc.scrapy.org/en/latest/topics/architecture.html中盗一张图来看Scrapy的运行机制,不多废话了。

image

创建Scrapy项目

终端执行scrapy startproject qq_qgc_spider,然后打开PyCharm导入项目即可。

分析页面结构

Chrome的“审查元素”功能派上用场了,还能一键导出XPath。贴张图感受一下。

image

保持登录状态

本来是想采用模拟登录的方法的,但是QQ群空间的网页版登录窗口在Chrome下显示不全,没有账号密码登录的选项,并且也没有单独的移动版页面,就只能采用提取Cookies的方法了。详情还是看图。

image

在settings.py中,加入默认请求headers,同时把COOKIES_ENABLED设为False,这样才会使用headers中定义的cookie。

# Disable cookies (enabled by default)
COOKIES_ENABLED = False

# Override the default request headers:
DEFAULT_REQUEST_HEADERS = {
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
    'Accept-Encoding': 'gzip, deflate',
    'Accept-Language': 'en,zh-CN;q=0.9,zh;q=0.8,ja;q=0.7,es;q=0.6',
    'Cache-Control': 'max-age=0',
    'Connection': 'keep-alive',
    'Cookie': 'pgv_pvi=2645434368; RK=XGpQSgnlP6; ptcz=db5efe1457bcd4488f4edc672e564bbc4343ba3b8330faed74e74bbc3c1545a1; pgv_pvid=484757304; o_cookie=305546990; pac_uid=1_305546990; luin=o0305546990;******************************************* ; uniqueuid=f79135c957a48801cf1a97a7667dc22f',
    'Host': 'qgc.qq.com',
    'Upgrade-Insecure-Requests': '1'
}

定义爬取数据结构

只需要帖子ID、标题、内容三项,所以items.py中这样写。

from scrapy import Field, Item

class QgcTopicItem(Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    topic_id = Field()
    title = Field()
    content = Field()
    pass

编写爬虫主程序

没有用BeautifulSoup和Selenium等爬虫工程中常用的库。一是它们的效率都不算很高;二是数据规模比较小,直接基于Scrapy的Selector+XPath做解析就行。
逻辑比较简单,帖子列表+帖子详情,典型的两级爬取结构。唯一特别要注意的是“下一页”逻辑的递归调用,另外meta可以用来方便传参。

#!/usr/bin/python
# -*- coding: utf-8 -*-

from scrapy import Spider
from scrapy.http import Request
from scrapy.selector import Selector
from qq_qgc_spider.items import *

QGC_ADDRESS = 'http://qgc.qq.com'
QQ_GROUP_ID = '89753069'

class QgcSpider(Spider):
    name = 'QgcSpider'
    allowed_domains = ['qq.com']
    start_urls = ['%s/%s?page=1' % (QGC_ADDRESS, QQ_GROUP_ID)]

    def parse(self, response):
        for url in self.start_urls:
            yield Request(url, self.parse_topic_list)

    def parse_topic_list(self, response):
        selector = Selector(response)

        a_links_titles = selector.xpath('//div[@id="threadlist"]/div[@class="feed clearfix"]/dl/dt/a')
        for a_link_title in a_links_titles:
            link = a_link_title.xpath('./@href').extract_first()
            title = a_link_title.xpath('./@title').extract_first()
            detail_request = Request(QGC_ADDRESS + link + '?hostOnly=1', self.parse_topic_detail)
            detail_request.meta['topic_id'] = link.split('/')[3]
            detail_request.meta['title'] = title
            yield detail_request

        a_page_numbers = selector.xpath('//div[@id="threadlist"]/div[@class="page"]/p/a')
        for a_page_no in a_page_numbers:
            span_no = a_page_no.xpath('./span/text()').extract_first()
            if span_no == u'下一页':
                link = a_page_no.xpath('./@href').extract_first()
                yield Request(QGC_ADDRESS + link, self.parse_topic_list)

    def parse_topic_detail(self, response):
        selector = Selector(response)
        content = ''

        div_contents = selector.xpath('//td[@id="plc_0"]/div[@class="pct xs2"]/div[@class="pctmessage mbm"]/div')
        for div_content in div_contents:
            div_paragraphs = div_content.xpath('./div//text()')
            for para in div_paragraphs.extract():
                content += (para + '\r\n')

        item = QgcTopicItem()
        item['topic_id'] = response.meta['topic_id']
        item['title'] = response.meta['title']
        item['content'] = content
        yield item

UA伪造和AutoThrottle

说到底都是为了防止被封。UA伪造可以使用fake_useragent库来实现。在middlewares.py中定义一个下载中间件。

class FakeUAMiddleware(object):
    def __init__(self, crawler):
        super(FakeUAMiddleware, self).__init__()
        self.ua = UserAgent()
        self.ua_type = crawler.settings.get('UA_TYPE', 'random')

    @classmethod
    def from_crawler(cls, crawler):
        return cls(crawler)

    def process_request(self, request, spider):
        fake_ua = getattr(self.ua, self.ua_type)
        request.headers.setdefault('User-Agent', fake_ua)

然后在settings.py中启用之。顺便还有AutoThrottle的设定,这样就不会跑得太快了。

DOWNLOADER_MIDDLEWARES = {
    'qq_qgc_spider.middlewares.FakeUAMiddleware': 543,
    'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None
}
# Enable and configure the AutoThrottle extension (disabled by default)
# See https://doc.scrapy.org/en/latest/topics/autothrottle.html
AUTOTHROTTLE_ENABLED = True
# The initial download delay
AUTOTHROTTLE_START_DELAY = 10
# The maximum download delay to be set in case of high latencies
AUTOTHROTTLE_MAX_DELAY = 60
# The average number of requests Scrapy should be sending in parallel to
# each remote server
AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0

将爬取结果保存到txt文件

用Scrapy的流水线来实现,每个帖子保存一个txt,并且采用GB18030编码,更平易近人一些。pipelines.py中加入:

import codecs
import sys

reload(sys)
sys.setdefaultencoding('utf-8')

class TextFilePipeline(object):
    def __init__(self):
        self.path = '/Users/lmagic/Documents/bsm_plays_backup/'

    def process_item(self, item, spider):
        file_name = '%s - %s.txt' % (item['topic_id'], item['title'])
        content = '%s\r\n\r\n%s'.decode('utf-8').encode('gb18030') % (item['title'], item['content'])
        fd = codecs.open(self.path + file_name, 'w+', encoding='gb18030')
        fd.write(content)
        fd.close()

settings.py中:

ITEM_PIPELINES = {
    'qq_qgc_spider.pipelines.TextFilePipeline': 300
    # 'qq_qgc_spider.pipelines.QgcSpiderPipeline': 300,
}

跑起来吧

from scrapy import cmdline

cmdline.execute("scrapy crawl QgcSpider".split())

然后去输出路径下收结果就好了~

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用Python+Scrapy爬取并保存QQ群空间帖子 的相关文章

  • java DateUtils时间工具栏

    package com eeye common utils import org apache commons lang3 time DateFormatUtils import java text ParseException impor
  • unity3d:Astar寻路,A星,A*,二叉堆优化Open表

    原理视频 油管 xff1a https youtu be i0x5fj4PqP4 别人的B站翻译 xff1a https www bilibili com video BV1v44y1h7Dt spm id from 61 333 999
  • TCP/IP 、HTTP、Socket的区别与联系

    1 Socket和http的区别 http xff1a 如何封装数据 xff1b 基于TCP协议 xff0c 简单的对象访问协议 xff0c 对应于应用层 xff1b xff08 货物 xff09 tcp协议 xff1a 数据在网络中的传输
  • 【亲测一次成功】将本地代码上传到Gitee码云

    1 在Gitee码云上创建仓库 2 在本地新建一个文件夹 3 右击新建的文件夹 xff0c 使用git bush here 4 输入 git init 5 输入 git remote add origin 43 仓库地址 6 输入 git
  • keil5打开工程报错:error:not found device

    1 error not found device 解决方法 xff1a 将Project文件夹中的工程扩展名由 uvproj改为 uvprojx 原因 xff1a 前提是所有的库安装都是正常且正确的 xff0c 所以应该是如下问题 xff1
  • STM32学习笔记:IWDG_独立看门狗

    1 简介 独立看门狗就是一个12位的递减计数器 xff0c 最大值0xFFF xff1b 计数器的值从某一个值减到0时 xff0c 系统产生一个复位信号 xff08 IWDG RESET xff09 xff1b 在计数器没减到0之前 xff
  • 基于单片机避障导盲智能拐杖控制设计(毕设资料)

    本设计研究为盲人提供行走时 xff0c 遇到前方障碍物提前躲避的智能避障预警系统 以AT89S52单片机作为核心处理器 xff0c 采用超声波回波时间差测量人与物体之间的安全距离 xff0c 实现了提前预警使用者避让障碍物 xff0c 起到
  • Matlab:excel文件 转 txt文件 (只需2行代码)

    亲测有用 xff0c 只需两行代码 xff0c 将EXCEL文件 xff0c 转换成txt文件 xff1a Data 61 readtable 39 TEST xls 39 writetable Data 39 test txt 39 ex
  • 快速理解C语言——指针

    1 地址和内存 把值存在内存中 xff0c 内存就给每一个值分配一个地址 xff1a 100 104 108 112 116就是每个值分别对应的地址 xff1b 给每个内存地址起个别名 xff0c 就是 xff1a 变量 2 值和类型 如下
  • 亲测有用!完美关闭win10不断自动更新

    自从更新到win10以来 xff0c 每次开关机都会遇到win10更新的问题 试过CSDN和其他很多种方法都没有用 xff0c 最后在知乎上看到一个大神写的 用以下方法完美解决 xff0c 再没出现过自动更新的问题 如何完美解决win10自
  • 一文解决所有PCA问题——这是我看过最好的讲解PCA理论文章

    转载 xff1a http blog codinglabs org articles pca tutorial html PCA xff08 Principal Component Analysis xff09 是一种常用的数据分析方法 P
  • C语言解析http协议

    C语言解析http协议 1 关键解析函数1 1 strstr xff08 xff09 1 2 strncmp 2 代码 1 关键解析函数 1 1 strstr xff08 xff09 函数原型 xff1a span class token
  • 大小端问题

    本来我想说 xff0c Windows平台一般是小端 xff0c Linux一般是大端 xff1b 但是 实际上大小端CPU架构有关 xff0c 当然和系统也可能有关 xff0c 可以配置大小端 xff1b 对于CPU框架 xff0c AR
  • android socket通讯

    项目中要用到进程间通讯 xff0c 服务端接收应用的请求数据 xff0c 对串口进行读写操作 考虑到android的socket服务比较实用 xff0c 并且可以支持多个客户端同时连接 服务端写成一个服务 xff0c 在init rc中启动
  • LwIP之套接字接口

    套接字结构体 struct lwip sock API连接指针 struct netconn conn 前一次读剩下的数据 void lastdata 前一次读数据的偏移量 u16 t lastoffset 接收数据的次数 s16 t rc
  • Simulink之功率场效应晶体管(P-MOSFET)

    功率场效应管 xff08 P MOSFET xff09 属于电压全控型器件 xff0c 门极静态电阻极高 驱动功率小 工作频率高 热稳定性好 xff1b 但是电流容量小 耐压低 功率不易做大 xff0c 常用于中小功率开关电路 电气符号 外
  • Simulink之变压器隔离的直流-直流变换器

    半桥式隔离降压变压器 全桥式隔离降压变压器
  • 动捕系统、ROS、SIMULINK的通信

    卓翼simulink控制源码 一 路径 xff1a droneyee ws src 下的功能包的作用 1 droneyee 包含无人机主要的起飞 降落的控制程序 xff1a Publisher的程序编写 matlab udp的IP和串口号的
  • USB描述符

    枚举过程 USB设备枚举一般会经过插入 供电 初始化 分配地址 xff0c 配置 xff0c 获取设备描述符 获取配置描述符 获取字符串和配置设备这么几个过程 xff08 第一次获取设备描述符就是为了获取最大包长 xff0c 在设备描述符的
  • 28335之GPIO输入

    include 34 DSP2833x Device h 34 include 34 DSP2833x Examples h 34 define LED GpioDataRegs GPADAT bit GPIO0 GPIO配置函数 void

随机推荐

  • 数字交流电源设计

    设计目标 xff1a 市电输入 开关频率32KHz 0 220V 50 100Hz 1200W输出 1 确定输入电压 经查阅 xff0c 我国市电电压标准 xff0c 220V单相供电时 xff0c 为额定值的 43 7 xff0c 10
  • 连续时间系统的时域分析

    一 微分方程的求解 1 求微分方程的齐次解 xff08 1 xff09 写出特征方程并求解 2 写出齐次解 2 求微分方程的特解 已知 xff08 1 xff09 根据表2 2 xff0c 写出特解函数 xff08 2 xff09 带入并求
  • CAN总线电平(隐性与显性)

    nbsp nbsp nbsp nbsp CAN2 0B规范定义了两种互补的逻辑数值 显性和隐性 同时传送显性和隐性位时 总线呈现显性状态 同时传送显性状态位时 总线呈现显性状态 同时传送隐性状态位时 总线呈现隐性状态 显性数值表示逻辑0 隐
  • STM32之通用定时器计数器模式

    include stm32f10x h RCC时钟配置 void RCC config ErrorStatus HSEStartUpStatus RCC寄存器设置为默认配置 RCC DeInit 打开外部高速时钟 RCC HSEConfig
  • STM32之通用定时器编码器模式

    1 编码器原理 如果两个信号相位差为90度 则这两个信号称为正交 由于两个信号相差90度 因此可以根据两个信号哪个先哪个后来判断方向 根据每个信号脉冲数量的多少及整个编码轮的周长就可以算出当前行走的距离 如果再加上定时器的话还可以计算出速度
  • Modbus寄存器地址规则

    Modbus协议定义的寄存器地址是5位十进制地址 xff0c 即 xff1a 线圈 xff08 DO xff09 地址 xff1a 00001 09999 触点 xff08 DI xff09 地址 xff1a 10001 19999 输入寄
  • 电赛TI处理器入门

    文章目录 电赛常用微处理器及评估板入门一 写在前面的话二 平台介绍1 TIVA C Series TM4C123G Lauchpad Evaluation Kit处理器芯片TM4C123GH6PM MCUARM架构处理器核心 Process
  • c++中的struct和class的区别

    1 struct与class的区别 1 继承权限 xff1a struct默认为public xff0c 而class默认的为private 2 访问权限 xff1a struct默认的成员变量访问控制权限是public xff0c 而cl
  • 常用字符串库函数总结

    本文转自https blog csdn net sharon 1987 article details 50022855 本文与原文内容没有差别 xff0c 但是由于本人比较注重颜值还有阅读体验 xff08 自认为这样可能阅读起来会舒服点
  • win7+linux双系统下删除linux系统

    装了Windows和linux双系统的朋友 xff0c 在后期要删除linux是个比较头痛的问题 xff0c 因为MBR已经被linux接管 xff0c 本文的目的是如何在windows 和linux双系统下 xff0c 简单 xff0c
  • 迭代器详解

    迭代器 前言一 可迭代对象 xff08 Iterable xff09 xff08 一 xff09 遍历对比 xff08 二 xff09 可迭代对象 xff08 Iterable xff09 1 确定可迭代对象2 确定共同属性3 错误 二 迭
  • GD32F130移植FreeRTOS

    最近淘到一块板子 xff0c 板载GD32F130C8T6 Cortex M3内核 xff0c 64KBFalsh 8KBSRAM 最近正在看FreeRTOS 就拿它来练练手 一 下载GD库文件 习惯了用STM32 xff0c 对GD3 1
  • Cy7c68013A速度测试教程

    手里有一个cypress的CY7C68013A模块 xff0c 一直没空玩 今天便测一下 xff0c 这个模块的USB2 0速率 1 开发工具下载 在cypress下载如下开发工具包 xff08 开发工具包下载地址 xff09 2 工具包安
  • Linux kernel编译

    bin bash echo 34 Configure the kernel 34 until echo 34 1 make the am335x lierda defconfig 34 echo 34 2 make the menuconf
  • Openwrt二级路由获取IPV6

    由于没有公网IPV4 便研究了一下公网IPV6 网上大部分是将光猫改为桥接 xff0c 然后路由拨号 xff0c 获取公网IPV6地址 xff0c 但目前不想这样做 研究一下 xff0c 二级路由下的IPV6获取 按照网上的说明 xff0c
  • 从RK3399的安卓系统中提取dts

    不久前淘到一块RK3399的板子 xff0c 安卓7 1的系统 xff0c 可是翻遍全网没有任何资料 便想着从系统中提取设备树文件 xff0c 自行适配linux系统 1 系统备份 参考该RK3328系统备份文章 xff0c 安装好驱动 x
  • MATALB 卷积神经网络 图片二分类

    正忙着写论文的时候 xff0c 突然看到她的询问 xff0c 连续两晚失眠 xff0c 有了这个程序 以前没用过神经网络 xff0c 所有代码都是基于别人基础上修改 xff0c 仅限于能实现自己需要的功能 从GitHub找到一个创建用于图像
  • MATLAB调用训练好的卷积神经网络

    上一篇链接 xff1a MATALB 卷积神经网络 图片二分类 上一篇已经介绍了如何对数据通过CNN进行深度学习分类 xff0c 并将训练好的模型保存下来 这里将介绍一下如何调用自己已经训练好的模型进行数据分类 1 加载模型 clear c
  • Unity URP DOTS Pathfinding+Local avoidance

    Unity URP DOTS Pathfinding 43 Local avoidance RVO2的效果还是蛮好的
  • 使用Python+Scrapy爬取并保存QQ群空间帖子

    首先声明 xff0c 在Python和爬虫这方面 xff0c 我是业余的那一卦 xff0c 只是平时玩一玩 xff0c 不能当真的 xff0c 请各位大佬轻拍 虽然爬虫与传统意义上的大数据技术不属于同一类 xff0c 但大概也只能放在大数据