「Python3 爬虫标准化项目」标准化爬虫数据抓取通用代码编写模板

文章目录

  • 内容介绍
  • Scrapy 模板操作流程
    • 项目创建操作
    • 项目文件配置
    • Spider 内容列表获取模板
    • Spider 内容详情获取模板
    • 执行抓取作业
    • 特别说明

内容介绍

开发环境为 Python3.6,Scrapy 版本 2.4.x ,Gerapy 版本 0.9.x ,爬虫项目全部内容索引目录

看懂Python爬虫框架,所见即所得一切皆有可能

既然是标准化作业,就必须要有一个标准化的模板。

依照此本文的模板可以做到无脑复制到Scrapy项目中,将每个spider文件修改 spider 目录下的每一个项目 py 文件即可。

只需要修改列表业页和详情页中需要每个页面抓取的部分,确定他们的标签和属性在模板中进行替换,即可实现单一网站整体快速的数据抓取和采集。

不过在这之前还是要确认好标准化作业中处理的爬虫抓取目标,精准定位。不仅抓取的数据可以便于项目中的应用,也方便后期的爬虫脚本的维护。

Scrapy 模板操作流程

项目创建操作

1. 创建项目

新建一个文件夹存放所有的 Scrapy 项目,打开命令行创建项目。

scrapy startproject 你的项目名(不支持中文)

我是按照抓取的网站类别进行分类创建。
「Python3 爬虫标准化项目」标准化爬虫数据抓取通用代码编写模板_第1张图片
「Python3 爬虫标准化项目」标准化爬虫数据抓取通用代码编写模板_第2张图片

2. 创建爬虫脚本

进入创建的项目文件夹创建爬虫脚本,即每个 spiders 目录下的文件。

创建每个spider文件注意事项。

  • spider 不支持中文、不要带标点符号。
  • spider 文件名称建议用网站的网址。
  • 作用域建议用空格代替先。

例如:http://www.cctd.com.cn/ 写成

scrapy genspider www_cctd_com_cn " "

「Python3 爬虫标准化项目」标准化爬虫数据抓取通用代码编写模板_第3张图片
3. 自动生成文件说明

  • scrapy.cfg
    项目的配置文件,可以暂时忽略。未来部署分布式会使用到。

  • items.py
    保存爬取到的数据的容器,其使用方法和python字典类似, 用于创建抓取内容的字段属性,比如新闻内容设置title、url、content属性用于保存数据结构。

  • pipelines.py
    用来执行保存数据的操作。用于根据items.py指定的字段提取的数据进行保存到相应的表格或者数据仓库中。

  • settings.py
    比较重要的配置文件,用于配置各种设置。

  • middlewares.py
    用于在执行爬虫脚本时进行相关操作的控制脚本,例如启动浏览器,更换代理IP等等操作。

  • spiders下的py文件
    是未来写好的爬虫执行脚本。

项目文件配置

1. 配置 items.py

  • 添加你要抓取内容的字段名称,根据自己的实际情况进行修改。
  • 定义的字段类型务必要与spider中抓取的字段相同,否则无法执行。
import scrapy

class StudydataItem(scrapy.Item):
    title = scrapy.Field() # 文章标题
    url = scrapy.Field() # 文章链接url
    thumbImg = scrapy.Field() # 文章封面
    publishTime = scrapy.Field() # 文章发布日期
    content = scrapy.Field() # 文章正文
    channel_name = scrapy.Field()  # 文章所属频道
    py_name = scrapy.Field() # 脚本名称
    web_name = scrapy.Field() # 网站名称

2.配置 middlewares.py

直接在底部添加对应内容。

2.1 添加中间件随机更换Header

用于随机更换浏览器头信息,防止被反爬的基础修改,这里是在 setting.py 文件中做好的浏览器header进行随机选择。

# 添加Header和IP类
from scrapy.downloadermiddlewares.useragent import UserAgentMiddleware
from scrapy.utils.project import get_project_settings

settings = get_project_settings()


class RotateUserAgentMiddleware(UserAgentMiddleware):
    def process_request(self, request, spider):
        referer = request.url
        if referer:
            request.headers["referer"] = referer
        USER_AGENT_LIST = settings.get('USER_AGENT_LIST')
        user_agent = random.choice(USER_AGENT_LIST)
        if user_agent:
            request.headers.setdefault('user-Agent', user_agent)
            print(f"user-Agent:{
       user_agent}")

2.2 设置更换代理IP

如果网站反爬不厉害的话这部分可以忽略。

# 添加随机更换IP代理类(根据实际IP代理情况进行修改获取方式和更改方式)
import random
import sys
import requests

sys.path.append('.')


class MyProxyMiddleware(object):
    def process_request(self, request, spider):
        url = "这里放购买的代理API地址,进行解析后使用代理访问"
        html = requests.get(url).text
        ip_list = html.split("\r\n")[:-1]
        proxy = random.choice(ip_list)
        request.meta['proxy'] = 'http://' + proxy

3.配置 pipelines.py

mongodb数据存储,毕竟抓上亿的数据感觉还是mongodb好用。这里为了安全起见为mongdb设置一下密码吧。

# 文件头添加
import pymongo
from scrapy.utils.project import get_project_settings
settings = get_project_settings()

这里是读取 Settings.py 中的MongoDB的相应设置操作,如果未在 Settings.py 中设置,这里也可以直接定义。

将settings[“xxxxx”]中的内容替换成你的配置即可。

# 添加必备包和加载设置
import pymongo
from scrapy.utils.project import get_project_settings
settings = get_project_settings()

class StudydataPipeline(object):
    # class中全部替换
    def __init__(self):
        host = settings["MONGODB_HOST"]
        port = settings["MONGODB_PORT"]
        dbname = settings["MONGODB_DATABASE"]
        sheetname = settings["MONGODB_TABLE"]
        username = settings["MONGODB_USER"]
        password = settings["MONGODB_PASSWORD"]
        # 创建MONGODB数据库链接
        client = pymongo.MongoClient(host=host, port=port, username=username, password=password)
        # 指定数据库
        mydb = client[dbname]
        # 存放数据的数据库表名
        self.post = mydb[sheetname]

    def process_item(self, item, spider):
        data = dict(item)
        # 数据写入
        self.post.insert(data)
        return item

4. 配置 settings.py
4.1 ROBOTSTXT_OBEY 机器人协议

文件中20-21行,建议修改Fasle否则很多网站没办法折腾。

# Obey robots.txt rules
ROBOTSTXT_OBEY = True

4.2 DOWNLOADER_MIDDLEWARES 下载中间件

  • 禁用自带的默认浏览器Header,一般创建文件时就默认禁用,无视。
  • 配置自定义的更换浏览器Herder中间件方法。其中 项目名 统一成你创建的项目名即可。
# Enable or disable downloader middlewares
# See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html
DOWNLOADER_MIDDLEWARES = {
     
    '项目名.middlewares.StudydataDownloaderMiddleware': 543,
    'scrapy.contrib.downloadermiddleware.useragent.UserAgentMiddleware': None,
    '项目名.middlewares.RotateUserAgentMiddleware': 400,  # 更换Header优先级
    # '项目名.middlewares.MyProxyMidleware': 300, #更换IP优先级默认注销掉
}

4.3 Item piplines 数据据处理

67-69行代码默认是注释的要解开,否则数据无法写入数据仓库。

# Configure item pipelines
# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
     
    '项目名.pipelines.StudydataPipeline': 300,
}

4.4 mongodb数据仓库配置

# 添加 设置MONGODB数仓
MONGODB_HOST = "你数据仓库的IP"
MONGODB_PORT = 数据仓库的端口号
MONGODB_DBNAME = "存储数据的数据仓库的仓库名"
MONGODB_SHEETNAME = "存储数据的数据仓库的表名"
MONGODB_USER = "mongdodb你设置的用户名"
MONGODB_PASSWORD = "mongdodb你设置的密码"

5. 随机浏览器header

可以根据自己需要进行增减。

# 添加 设置浏览器Header
USER_AGENT_LIST = [
      "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36",
      "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1",
      "Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11",
      "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6",
      "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6",
      "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1",
      "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5",
      "Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5",
      "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
      "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
      "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
      "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
      "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
      "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
      "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
      "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
      "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3",
      "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24",
      "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24"
]

Spider 内容列表获取模板

实现一个列表页中根据列表的顺序进行遍历循环抓取每个url并访问然后存储到数据仓库中。

这个模板根据自己的需要进行调整即可。

1. 设置第三方加载包

import scrapy
from 项目名.items import 对应项目的item(就是items.py里的class名)
import json
import time
import re
from urllib import parse
from .parse_detail import *
from random import shuffle
from gerapy_auto_extractor.extractors import *
from scrapy.utils.project import get_project_settings
settings = get_project_settings()

2. 设置基础数据属性

    name = 'xxxxxx'  # 这个地方是默认生成用于启动脚本的name不需要修改
    allowed_domains = [] # 作用域,这里之前定义成空格,这里就不需要操作了

    web_name = "xxxxxx网"  # 抓取的网站中文名称,用于爬虫数据管理
    start_menu = [
        # 抓取栏目类别标记,比如写成 XXX频道 这样
        # 也可以把同一CSS样式的放一起通过同一个 parse 处理
        [
            # 这里放处理好的数据字典
            # 数据格式举例
        	{
     "channel_name": "xxxxx频道-xxxxx列表", "url": "https://www.xxxx.com/list.html", },
        ],
    ]

3.start_requests方法重写

重写 start_requests 方法,依据2中的start_menu格式进行处理。

    def start_requests(self):
    	# start_menu 中的1个列表元素对应 parse_list 列表元素。
    	# 意思是对应每个列表元素执行对应的 parse 方法进行页面解析处理。
        parse_list = [
            self.parse1,
        ]

        # 非API接口方法,增加随机处理模式
        start_menu_list = self.start_menu
        shuffle(start_menu_list)  # 打乱每个栏目种的顺序
        for each_menu_num in range(len(self.start_menu)):
            new_list = self.start_menu[each_menu_num]
            shuffle(new_list)  # 打乱每个栏目中的抓取目标
            for each_menu in self.start_menu[each_menu_num]:
                url = each_menu["url"]
                channel_name = each_menu["channel_name"]
                parse_function = parse_list[each_menu_num]

                yield scrapy.Request(
                    url=url,
                    meta={
     
                        'url': url,
                        'channel_name': channel_name,
                    },
                    callback=parse_function
                )

4. parse 列表页数据处理

  • 常规的定位抓取目标的属性和标签。
    # 抓取内容的配置 parse_list根据定义匹配
    def parse1(self, response):
        # 抓取选择页面的列表信息,获取的内容均为list,要求长度必须一直否则会出错
        # 这里不抓取日期,在正文里抓取日期
        Item_title = response.xpath('//标签[@class="属性"]/a/text()').extract()  # 文章标题列表
        Item_url = response.xpath('//标签[@class="属性"]/a/@href').extract()  # 文章链接列表
        Item_thumbImg = response.xpath('//标签[@class="属性"]/a/img/@src').extract()  # 文章封面图片列表

        for each in range(len(Item_title)):
            item = 对应项目的item()  # 这里对应Item里的类名
            item['title'] = Item_title[each].strip()  # 内容标题
            item['url'] = parse.urljoin(response.url, Item_url[each])  # 拼接正文url
            # item['thumbImg'] = parse.urljoin(response.url, Item_thumbImg[each]) # 拼接图片url
            item['publishTime'] = ""
            item['thumbImg'] = ""
            item['channel_name'] = response.meta["channel_name"]
            item['web_name'] = self.web_name
            item["py_name"] = self.name + ".py"
            yield scrapy.Request(item['url'], callback=self.parse_detail, meta={
     'item': item})
  • 使用 gerapy_auto_extractor 直接获取页面中标题和列表,但是某些网页无法获取。
    # 抓取内容的配置 parse_list根据定义匹配
    def parse1(self, response):
        data = extract_list(response.text)
        for each in range(len(data)):
            item = 对应项目的item()  # 这里对应Item里的类名
            item['title'] = data[each]["title"].strip()  # 内容标题
            item['url'] = parse.urljoin(response.url, data[each]["url"])  # 拼接正文url
            # item['thumbImg'] = parse.urljoin(response.url, Item_thumbImg[each])  # 拼接图片url
            item['publishTime'] = ""
            item['thumbImg'] = ""
            item['channel_name'] = response.meta["channel_name"]
            item['web_name'] = self.web_name
            item["py_name"] = self.name + ".py"
            yield scrapy.Request(item['url'], callback=self.parse_detail, meta={
     'item': item})

5.详情页内容回调方法

将抓取内容详情页的方法统一交付到 parse_detail.py 文件处理。

    # 详情页在parse_detail.py中处理
    def parse_detail(self, response):
        item = ProcessContent(self, response)
        yield item

Spider 内容详情获取模板

在目录下新建文件 parse_detail.py

用于处理抓取的内容的日期和正文数据,其中使用 gerapy_auto_extractor 模块偷懒直接获取正文内容和日期(前提正文内容里要有)

# coding:utf-8
__author__ = 'Mr.数据杨'
__explain__ = '抓取文章正文内容' \
              '1.DateTimeProcess_Str 处理日期内容,如果始终没有日期可以抓取默认当天日期' \
              '2.ProcessContent 处理正文内容' \
              '3.提取unHtmlContent(无格式的文字内容)' \
              '4.提取content(含有CSS样式的内容)'

import re
from gerapy_auto_extractor.extractors import extract_detail
import time


# 处理日期数据函数
def DateTimeProcess_Str(text):
    time_text = str(text)
    if ("年" or "月" or "日") in time_text:
        time_text = re.findall('\d{4}年\d{2}月\d{2}', time_text)[0]
        time_text = re.sub(r'[年月]', '-', time_text)
        time_text = re.sub(r'[日]', '', time_text)
        return time_text
    elif "-" in time_text:
        time_text = re.findall('\d{4}-\d{2}-\d{2}', time_text)[0]
        return time_text
    else:
        return time.strftime('%Y-%m-%d')


# 判断内容是否None
def None2Str(text):
    if text is None:
        return ''
    else:
        return text


def ProcessContent(self, response):
    # 设置详情页的内容
    item = response.meta['item']

    data = extract_detail(response.text)

    # 处理详情页的时间,如果始终没有获取到时间默认当天日期
    if data["datetime"] is None:
        item['publishTime'] = DateTimeProcess_Str(item['publishTime'])
    else:
        item['publishTime'] = DateTimeProcess_Str(data["datetime"])

    # 处理详情页带格式,这里整个页面进行抓取
    item['content'] = ""
    # 这里对应的是列表抓取文件下的内容,分不同的网站设置不同的详情抓取方法。
    if item['web_name'] == "xxxxxx": 
        if '属性="xxxxxx"' in response.text and len(None2Str(item['content'])) < 5:
            item['content'] = response.xpath('//标签[@属性="xxxxxx"]').extract_first()

    # 通用的方法页面数据无法采集默认抓取整个页面
    if len(item['content']) < 5:
        item['content'] = response.xpath('//body').extract_first()

    return item

执行抓取作业

1.启动脚本

scrapy crawl (spider下文件名,启动脚本)

scrapy crawlall (spider全部,启动脚本,需要进行配置)

在这里插入图片描述

2.抓取的过程
「Python3 爬虫标准化项目」标准化爬虫数据抓取通用代码编写模板_第4张图片
3.抓取结果
「Python3 爬虫标准化项目」标准化爬虫数据抓取通用代码编写模板_第5张图片

特别说明

有些网站的程序员丧心病狂到一定程度10个页面9种样式这种,由于我们不可能每个页面都打开看一下详情页的CSS格式,因此有个通用的解决办法。

  • 第一次抓取完内容之后打开MongoDB数据库执行下面的命令会把包含body的页面数据筛选出来,这些是没有根据指定样式抓取的数据,而是直接抓的全部页面的数据。
  • 打开任意的link循环处理详情页的内容直到mongo命令没有筛选出来内容为止即可。
  • mongodb命令,查找包含body标签的文章,显示的数据即为为定义的样式的内容。
db.你的表名.find({content:/body/})

你可能感兴趣的:(Python,爬虫基础和项目管理,python,scrapy,爬虫模板,爬虫入门,爬虫案例)