一个人,一座城,你到底在乎什么?Python 爬虫告诉你!

news2024/11/26 8:48:19

e0b812240b22297e848c28a0f7cb72f8.png

大家好,我是安果!

有时候,我们想知道生活在这座城市的人每天交流的事情,然后对数据进行一些分析,方便我们更好地了解城市的特征及居民的需求

以重庆为例,最火爆的论坛是购物狂,每天都有大量的帖子内容产生,基本上囊括了重庆人的衣食住行

本篇文章将介绍使用 Scrapy 爬取该论坛数据的完整教程

1. 爬虫

目标对象:

aHR0cHM6Ly9nby5jcW1tZ28uY29tL3RocmVhZC9sYXN0ZXN0P3BhZ2U9MQ==

我们需要爬取所有页面帖子中所有的回帖信息

1-1  安装依赖

# 安装依赖
pip3 install Scrapy

# 生成词云
pip3 install jieba
pip3 install stylecloud

1-2  创建项目及爬虫

# 创建项目
scrapy startproject cq_all

# 创建一个 CrawlSpider 爬虫
cd cq_all
scrapy genspider -t crawl talk "HOST"

1-3  定义 Item 实体对象

在 items.py 文件中,将需要爬取的数据定义为 Item

这里只需要爬取帖子的标题、URL 及所有回帖内容

# items.py

import scrapy

class CqAllItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    title = scrapy.Field()  # 帖子标题
    url = scrapy.Field()  # URL
    content = scrapy.Field()  # 内容

1-4  编写爬虫

在 spiders 文件夹下的爬虫文件中编写具体的爬虫逻辑

首先通过定义 rules,指定要爬取的地址,然后使用 Xpath 语法解析出所有文本内容

PS:通过设定 follow 为 True,可以爬取某一个帖子所有的回帖数据

# talk.py

from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule

class TalkSpider(CrawlSpider):
    name = 'talk_all'
    allowed_domains = ['HOST']
    start_urls = ['https://HOST/thread/lastest?page={}'.format(i + 1) for i in range(20)]

    rules = (
        Rule(LinkExtractor(allow=r'https://HOST/forum-\d+-thread-\S+.html', deny=r'safeRedirect'),
             callback='parse_forum',
             follow=True),
    )

        def parse_forum(self, response):
        """
        解析帖子
        :param response:
        :return:
        """
        try:
            # 帖子标题
            title = response.xpath('//*[@id="view-hd"]//span/text()').extract()[0]

            # URL
            url = response.url

            # 获取内容,并获取其真实文字内容
            content = get_content(
                response.xpath('//*[@id="view-bd"]//table[@class="view-data"]//div//text()').extract())

            # 导航标题栏
            tab_titles = response.xpath('//*[@id="nav"]//a/text()').extract()

            # 过滤特殊帖子,比如:公告
            if not in_exclude_tabs(tab_titles):
                if content:
                    # print("帖子URL:", url, ",标题:", title, ",内容:", content)
                    item = CqAllItem()
                    item['title'] = title
                    item['url'] = url
                    item['content'] = content
                    yield item
                else:
                    # print("(过滤掉)帖子URL:", response.url, "内容为空")
                    pass
            else:
                # print("(过滤掉)过滤的帖子,Tab:", tab_titles, ",地址:", response.url)
                pass
        except Exception as e:
            print("产生异常,异常信息:", str(e))

为了过滤无效信息,这里对回帖内容及 Tab 进行一次初步过滤

# 排除的Tab,过滤无效信息
exclude_tabs = ['公告','网友中心']

def get_content(contents):
    """
    去掉空格换行符
    :param contents:
    :return:
    """
    result = ''
    for item in contents:
        result += item.strip()
    return result


def in_exclude_tabs(tab_titles):
    result = False
    for tab_title in tab_titles:
        if tab_title in exclude_tabs:
            result = True
            break
    return result

1-5  反反爬

为了应对网站的反爬,这里需要进行如下处理

  • 下载延迟

  • 自定义请求头

  • 禁用重定向

# settings.py

# Obey robots.txt rules
ROBOTSTXT_OBEY = False

DOWNLOAD_DELAY = 1

# 禁用重定向
REDIRECTS_ENABLED = False

# talk.py
# 自定义请求头
custom_settings = {
        "COOKIES_ENABLED": False,
        "DOWNLOAD_DELAY": 3,
        'DEFAULT_REQUEST_HEADERS': {
            'Accept': '*/*',
            'Accept-Encoding': 'gzip, deflate, br',
            'Accept-Language': 'zh-CN,zh;q=0.9',
            'Connection': 'keep-alive',
            'Content-type': 'application/x-www-form-urlencoded;',
            'Host': 'go.cqmmgo.com',
            'Origin': 'https://HOST',
            'sec-ch-ua': '"Not?A_Brand";v="8", "Chromium";v="108", "Google Chrome";v="108"',
            'sec-ch-ua-mobile': '?0',
            'sec-ch-ua-platform': "Windows",
            'Sec-Fetch-Mode': 'cors',
            'Sec-Fetch-Site': 'same-origin',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36',
            'Referer': 'https://HOST/',
            'Cookie': '*',
        }
    }

1-6  自定义下载管道 Pipline

在 piplines.py 文件中,自定义 2 个下载管道,分别将数据写入到本地 CSV 文件和文本文件中

from scrapy.exceptions import DropItem
from scrapy.exporters import CsvItemExporter

from cq_all.items import CqAllItem


class DuplicatesPipeline(object):
    """
    Pipline去重复的帖子
    """

    def __init__(self):
        self.talk_set = set()

    def process_item(self, item, spider):
        name = item['title']
        if name in self.talk_set:
            raise DropItem("Duplicate book found:%s" % item)
        self.talk_set.add(name)
        return item


class CqAllPipeline(object):
    """保存CSV"""

    def __init__(self):
        self.file = open("./cq_all.csv", 'wb')
        self.exporter = CsvItemExporter(self.file, fields_to_export=[
            'title', 'url', 'content'
        ])
        self.exporter.start_exporting()

    def process_item(self, item, spider):
        if isinstance(item, CqAllItem):
            self.exporter.export_item(item)
        return item

    # 关闭资源
    def close_spider(self, spider):
        self.exporter.finish_exporting()
        self.file.close()


class CqAllTxtPipline(object):
    """
    保存到txt文件中
    """

    def __init__(self):
        self.file = open('result.txt', 'w', encoding='utf-8')

    def open_spider(self, spider):
        # self.file = open('result.txt', 'w', encoding='utf-8')
        pass

    def close_spider(self, spider):
        self.file.close()

    def process_item(self, item, spider):
        try:
            if isinstance(item, CqAllItem):
                self.file.write(item['content'] + '\n')
        except:
            pass

1-7  配置爬虫配置文件

打开 settings.py 文件,配置默认请求头及数据管道

# settings.py

# Override the default request headers:
DEFAULT_REQUEST_HEADERS = {
    'Accept': '*/*',
    'Accept-Encoding': 'gzip, deflate, br',
    'Accept-Language': 'zh-CN,zh;q=0.9',
    'Connection': 'keep-alive',
    'Content-type': 'application/x-www-form-urlencoded;',
    'Host': 'HOST',
    'Origin': 'https://HOST',
    'sec-ch-ua': '"Not?A_Brand";v="8", "Chromium";v="108", "Google Chrome";v="108"',
    'sec-ch-ua-mobile': '?0',
    'sec-ch-ua-platform': "Windows",
    'Sec-Fetch-Mode': 'cors',
    'Sec-Fetch-Site': 'same-origin',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36',
    'Referer': 'https://HOST/'
}

ITEM_PIPELINES = {
    'cq_all.pipelines.DuplicatesPipeline': 1,
    'cq_all.pipelines.CqAllPipeline': 2,
    'cq_all.pipelines.CqAllTxtPipline': 3,
}

1-8 爬虫主入口

在爬虫项目根目录下创建一个文件,通过下面的方式运行单个爬虫

# main.py

from scrapy.cmdline import execute
import sys, os

def start_scrapy():
    sys.path.append(os.path.dirname(__file__))
    # 运行单个爬虫
    execute(["scrapy", "crawl", "talk_all"])

def pre():
    """
    删除临时文件
    :return:
    """
    if os.path.exists('cq.png'):
        os.remove("cq.png")

    if os.path.exists('result.txt'):
        os.remove('result.txt')

if __name__ == '__main__':
    pre()
    start_scrapy()

2. 词云可视化

在爬虫类重写 close 方法,根据本地文件绘制成词云图片保存到本地

# talk.py

class TalkSpider(CrawlSpider):
  def close(spider, reason):
    # 绘制词图
    gene_word_cloud()
    return None

2-1  内容预处理

从爬取文本中读取数据后进行预处理,过滤一些无用的字符

def gene_word_cloud():
    """根据文件生成词云"""
    with open('result.txt', 'r', encoding='utf-8') as f:
        data = f.read()
        # 文本预处理
        new_data = re.findall('[\u4e00-\u9fa5]+', data, re.S)
        new_data = "/".join(new_data)

2-2  分词后去除单词

接着使用 jieba 进行分词,然后过滤掉单词

import jieba, stylecloud

# 解析
word_list_pre = jieba.cut(new_data, cut_all=True)

# 去除单词
word_list = [item for item in word_list_pre if len(item) > 1]

result = " ".join(word_list)  # 分词用空格隔开

2-3  停用词汇

为了保证生成词云的有效性,我们一般需要自定义停用词汇

这里推荐下面 2 个词汇,大家可以在此基础上进行二次编辑

https://github.com/goto456/stopwords/blob/master/cn_stopwords.txt

https://github.com/fwwdn/sensitive-stop-words/blob/master/stopword.dic

def get_cn_stop_words():
    """
    从文件中读取停用中文词
    :return:
    """
    stop_words = set()
    # 停用词汇
    # https://github.com/fwwdn/sensitive-stop-words/blob/master/stopword.dic
    # https://github.com/goto456/stopwords/blob/master/cn_stopwords.txt
    with open('cn_stopwords.txt', encoding='utf-8') as f:
        con = f.readlines()
        for i in con:
            i = i.replace("\n", "")  # 去掉读取每一行数据的\n
            if i:
                stop_words.add(i)
    return list(stop_words)

2-4  绘制词云

使用 stylecloud 绘制词云的时候,我们需要指定图片大小、字体、调色方案、方向、蒙版、停用词汇等参数

其中

调色方案参考:

https://jiffyclub.github.io/palettable/cartocolors/qualitative/

词云蒙版参考:

https://fa5.dashgame.com/#/

import jieba, stylecloud

image_path = 'cq.png'

    # 中文禁用词
    stop_words = get_cn_stop_words()

    stylecloud.gen_stylecloud(
        text=result,  # 上面分词的结果作为文本传给text参数
        size=1024,
        font_path='msyh.ttc',  # 字体设置
        palette='cartocolors.qualitative.Pastel_10',  # 调色方案选取,从palettable里选择
        gradient='horizontal',  # 渐变色方向选了垂直方向
        icon_name='fas fa-dove',  # 蒙版选取,从Font Awesome里选
        stopwords=True,  # 布尔值,用于筛除常见禁用词
        max_words=200,
        collocations=False,
        custom_stopwords=stop_words,
        output_name=image_path)

2-5  消息推送

我们读取本地图片,使用企业微信机器人发送出去

import re, base64, hashlib
import requests

def send_wx():
    """
    将词云图片发送到微信
    :return:
    """
    with open(image_path, 'rb') as file:  # 转换图片成base64格式
        data = file.read()
        image_data = str(base64.b64encode(data), 'utf-8')

    with open(image_path, 'rb') as file:  # 图片的MD5值
        md = hashlib.md5()
        md.update(file.read())
        image_md5 = md.hexdigest()

    url = 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xx'
    headers = {"Content-Type": "application/json"}
    data = {
        "msgtype": "image",
        "image": {
            "base64": image_data,
            "md5": image_md5
        }
    }
    result = requests.post(url, headers=headers, json=data)
    return result

3. 最后

在本机运行成功之后,我们就可以将程序部署到服务器了

需要注意的是,服务器一般没有中文字体,我们需要额外安装字体

以 CentOS 为例,我们首先需要安装 fontconfig 和 ttmkfdir,然后下载中文字体放置到中文字体目录下,接着修改配置文件 fonts.conf,最后刷新字体缓存即可

关于文中出现的任何疑惑,欢迎大家在文末留言交流!

推荐阅读

5 分钟,教你用 Python 制作一个生日提醒!

实战 | 教你快速爬取热门股票,辅助量化交易!

实战 | 如何利用 Scrapy 编写一个完整的爬虫!

END

好文和朋友一起看~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/167806.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

求解带不确定事件的FJSP的多目标强化学习框架

文献:Hao Wang, Junfu Cheng, Chang Liu, Yuanyuan Zhang, Shunfang Hu, Liangyin Chen,Multi-objective reinforcement learning framework for dynamic flexible job shop scheduling problem with uncertain events,Applied Soft Computing,Volume 131,2022,1097…

超级详细的python知识点及练习题(附答案)

今天咱们继续来学习python的小知识吖,上一次木有看的同学请看:python8大核心语句 作者:阿玥的小东东 学习:python,正在学习c 主页:阿玥的小东东 目录 1.复习及易错,快来学习!&#…

基于python手撕实现BP 神经网络实现手写数字识别

本项目使用python实现全连接网络和梯度优化 方向传播并且实现了 手写数字识别项目: 神经网络 model 先介绍个三层的神经网络,如下图所示输入层(input layer)有三个 units( 为补上的 bias,通常设为

线程池ThreadPoolExecutor源码解析

参考视频 首先回顾一下创建线程等的三种方式 第一个是直接继承Thread类,重写run方法,这个其实内部也是继承了Runnable接口重写run方法。 比如: public class MyThread extends Thread{Overridepublic void run() {System.out.println("…

论文分享-《基于数据驱动多输出 ARMAX 建模的高炉十字测温中心温度》

1.简介 最近在学习研究NARMAX,故也分享下自己看的一篇论文。 2018 年 3 月 的《基于数据驱动多输出 ARMAX 建模的高炉十字测温中心温度》。主要是采用NARMAX模型进行预测,多输入多输出,有5个输出,预测中心五个点位的温度。下面讲…

计算机 - - - 局域网共享文件夹,局域网传输文件(待完善)

win10局域网共享文件夹 A电脑: 共享文件夹的电脑 B电脑: 访问共享文件夹的电脑 操作完成后, B电脑可以下载A电脑中的文件, B电脑可以修改删除, B电脑可以上传B电脑的文件到A电脑. A电脑 找到要共享的文件夹, 例如我要共享文档(E:), 我要把文档(E:)中的所有文件都让B电脑访问…

Python - 数据容器str(字符串)

目录 字符串的定义 字符串的常用操作 查找特定字符串的下标索引值 index 字符串的替换 replace 字符串的分割 split 字符串的规整操作 strip 统计字符串中某字符串的出现次数 count 统计字符串的长度 len 字符串切片 [起始下标:结束下标:步长] 字符串的定义 和其它容器…

银行案例分析:识别个人贷款客户画像,实现精准营销与风险防范

作为商业银行最主要的业务活动,也是收益最大的活动,贷款于银行的重要性不言而喻。又由于个人贷款是银行贷款不可或缺的一部分,那么了解个人贷款客户画像就有助于银行对客户进行精准销售和风险识别。 # 选手介绍 #张昊泽:亚利桑那州…

Pycharm入门搭建Django 项目

一、环境准备 1、pycharm版本 2、python版本 二、创建项目 击左上角的 File -> New Project 点击Create创建完成之后页面等待下载环境 查看Django的版本 python -m django --version 启动项目 python manage.py runserver 三、后记 在启动 Django 项目的时候我发现控制台…

【PaaS】分享一家最近发现的宝藏Paas厂家

目录 一、结识独自开 二、独自开的介绍 三、独自开的需求 四、独自开注册流程 五、神仙公司独自开 一、结识独自开 算是机缘巧合,我被C站白佬拉入了他的聊天群,群内均是来自于CSDN的不同领域的优质作者,其中不乏相关领域工作多年的老工程…

第一层:封装

文章目录前言类和对象封装class权限publicprotectedprivatestruct和class的区别封装的好处封装的用法学完封装,突破第一层🎉welcome🎉 ✒️博主介绍:一名大一的智能制造专业学生,在学习C/C的路上会越走越远&#xff0c…

【电子学会】2022年12月图形化四级 -- 金牌百分比

金牌百分比 计算金牌榜前十的国家获得的金牌总数占金牌总数的百分比。金牌榜前十的国家获得的金牌总数占金牌总数的百分比等于(金牌榜前十国家的金牌总数本届冬奥会金牌总数)100,并将这个数向下取整。 1. 准备工作 (1)保留舞台默认背景及角色小猫; (2)建立变量“金牌…

YOLOALL 一文了解YOLO各版本答案

来源:投稿 作者:ΔU 编辑:学姐 YoloAll项目简介 相信了解YOLO的小伙伴们一定都有这样的困扰,目前YOLO各个版本数量非常多,不知道在实际场景中应该选择哪个YOLO版本。甚至有时为了比较两个不同版本的YOLO的效果&#x…

python标准库xmlrpc 之RPC远程方法调用

💖💖💖养成每日阅读好习惯, 每天进步, 超越昨天的自己💖💖💖 愿景:输出体系化编程知识与技巧,助力软件行业发展与从业者学习减负,让编程产生更大价值。 🔎&am…

【linux】之网络安全

防火墙作用 在计算机领域,防火墙是用于保护信息安全的设备,其会依照用户定义的规则,允许或限制数据的传输。 用于保护内网安全的一种设备依据规则进行防护用户定义规则允许或拒绝外部用户访问防火墙分类 逻辑上划分,防火墙可以大体分为主机防火墙和网络防火墙 主机防火墙:…

服务注册与发现:Nacos Discovery

目录 一、概述 二、Nacos discovery——服务的注册与发现 1. 版本关系 2. 下载安装 (1)下载 (2)启动 (3)浏览器访问 三、Nacos服务注册与发现实战 1. 构建Spring Cloud Alibaba工程 (1&…

Vite性能优化之分包策略

为什么需要分包策略? 浏览器的缓存策略 浏览器在请求静态资源时,只要静态资源的名称不变,它就不会重新请求 xxx.js资源。 使用Vite打包后的js文件是带有哈希值的,只要我们的代码内容有一点点变化,那么文件的hash值都…

ConfigMap

目录 文章目录目录本节实战前言1、创建:warning: yaml里易混淆的点(1)|用法(2)|和|-用法(3)>用法1.通过资源清单文件方法创建ConfigMap2.通过from-file关键字创建ConfigMap(1)fro…

二十九、Kubernetes中CronJob(CJ)详解

1、概述 在kubernetes中,有很多类型的pod控制器,每种都有自己的适合的场景,常见的有下面这些: ReplicationController:比较原始的pod控制器,已经被废弃,由ReplicaSet替代 ReplicaSet&#xff…

yolov5训练自己的数据集,OpenCV DNN推理

学更好的别人, 做更好的自己。 ——《微卡智享》 本文长度为4238字,预计阅读9分钟 前言 上一篇《OpenCV--自学笔记》搭建好了yolov5的环境,作为目标检测在应用中,最重要的还是训练自己的数字集并推理,所以这一篇就专门…