欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【Scrapy 5分钟撸网站系列】数据抓取项目框架通用模板

程序员文章站 2022-05-08 16:57:19
...

整理模板说明

业务目标及操作

按照这个模板无脑复制到你的Scrapy项目中,每个spider文件需要修改的地方只有获取列表页数据和详情页数据中确定他们的标签和属性之后直接替换模板中的内容就可以实现快速完成抓取业务。

其中数据处理部分要先讲需要抓取的 url 字典数据整理好,然后根据下面的指引5分钟一个网站哗啦出来不是问题。

数据处理部分参考这里的操作进行。
【Scrapy 5分钟撸网站系列】爬虫目标整理和数据准备

了解学习全部Scrapy模块学习请点传送门
【Scrapy 2.4.0 文章目录】源码解析篇:全部配置目录索引

Scrapy 模板操作流程

项目创建shell

创建项目目录
找一个文件夹放爬虫文件。

scrapy startproject 项目名  

创建爬虫文件
这里建议用空格的第二种方法,为啥看后面的部分。

scrapy genspider 脚本名称 "目标网站"

scrapy genspider 脚本名称 " "  

执行文件操作
这部分使用第一个调试每个脚本能正常抓取之后会通过Django部署,请看部署篇。

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

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

项目默认文件生成配置

配置 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() # 网站名称

配置 middlewares.py

直接在底部添加对应内容

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. 更换代理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

配置 pipelines.py

mongodb数据存储

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

# 文件头添加
import pymongo
from scrapy.utils.project import get_project_settings
settings = get_project_settings()
# 添加必备包和加载设置
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

配置 settings.py

1. ROBOTSTXT_OBEY 机器人协议

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

# Obey robots.txt rules
ROBOTSTXT_OBEY = True

2. DOWNLOADER_MIDDLEWARES 下载中间件

禁用自带的,使用我们自定义的更换浏览器Herder中间件方法。
StudyData 是项目名,根据你的项目修改即可。

# Enable or disable downloader middlewares
# See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html
DOWNLOADER_MIDDLEWARES = {
    'StudyData.middlewares.StudydataDownloaderMiddleware': 543,
    'scrapy.contrib.downloadermiddleware.useragent.UserAgentMiddleware': None,
    'StudyData.middlewares.RotateUserAgentMiddleware': 400,  # 更换Header优先级
    # '项目名.middlewares.MyProxyMidleware': 300, #更换IP优先级默认注销掉
}

3. Item 管道数据处理

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

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

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模板

Spider下每个py文件

实现一个列表页中根据列表的顺序进行遍历循环抓取每个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 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接口方法
        for each_menu_num in range(len(self.start_menu)):
            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 = CoalindustryItem()  # 这里对应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})

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

parse_detail.py

在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 '属性="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