Scrapy入门

1. 准备环境

安装virtualenv,使用virtualenv生成一个虚拟环境:

mkvirtualenv scrapy_env

使用pip安装scrapy:

pip install scrapy

在安装scrapy的时候有可能会出现以下异常:

error: Microsoft Visual C++ 14.0 is required. Get it with "Microsoft Visual C++ Build Tools": https://visualstudio.microsoft.com/downloads/

此时,需要手动安装Twisted-***.whl才可以。Twisted-***.whl可以从以下网站中获取:
https://www.lfd.uci.edu/~gohlke/pythonlibs/
注意,需要下载与本地Python版本以及计算机环境相符合的版本。

下载完之后,将其放到scrapy_env虚拟环境的根目录下(放在别的地方也无所谓),然后将cmd的路径切换到同一目录,执行以下命令进行安装:

pip install Twisted-18.9.0-cp37-cp37m-win_amd64.whl

安装完成后,再次安装scrapy即可。

至此,环境准备完成。

2. 生成scrapy项目

将cmd路径切换到工作目录,然后执行以下命令,即可创建scrapy项目:

scrapy startproject [项目名]

然后我们将工作目录切换到刚创建的项目名下面,创建一个爬虫:

cd [项目名]
scrapy genspider [爬虫名] [域名]
(例如:scrapy genspider jobbole jobbole.com)

执行命令之后,scrapy会在spiders目录下自动生成一个[爬虫名].py的文件,内容如下:

# -*- coding: utf-8 -*-
import scrapy

class JobboleSpider(scrapy.Spider):
    name = 'jobbole'
    allowed_domains = ['jobbole.com']
    start_urls = ['http://www.jobbole.com/']

    def parse(self, response):
        pass

至此,我们就完成了爬虫项目的初始化工作。

3. 爬取数据

我们要爬取http://python.jobbole.com/all-posts/下的所有文章的标题、发布时间、类别。

3.1 创建Item

# -*- coding: utf-8 -*-

# Define here the models for your scraped items
#
# See documentation in:
# https://doc.scrapy.org/en/latest/topics/items.html

from scrapy import Item, Field
from scrapy.loader import ItemLoader
from scrapy.loader.processors import MapCompose, TakeFirst
from item_filters.jobbole_filters import date_filter


class CommonItemLoader(ItemLoader):
    """
    继承ItemLoader,为Item添加一个默认的output_processor
    """
    default_output_processor = TakeFirst()

class JobboleItem(Item):
    title = Field()
    publish_date = Field(input_processor=MapCompose(date_filter))
    category = Field()
    url = Field()

3.2 自定义input_processor

在获取publish_date的时候,需要用到自定义input_processor。我另外创建了一个文件,专门用于保存自定义input_processor函数。

import re
from w3lib.html import remove_tags


def date_filter(value: str) -> str:
    """
    从tag中提取出YYYY/MM/DD的字符串
    :param value: tag字符串
    :return: YYYY/MM/DD
    """
    value = remove_tags(value)
    pattern = re.compile(r'\d{4}/\d{2}/\d{2}')
    date: str = pattern.findall(value)[0]
    return date

3.3 写爬虫

在jobbole.py文件中这样来写:

# -*- coding: utf-8 -*-
import scrapy
from scrapy import Request, Selector
from scrapy.http import HtmlResponse
from typing import List
from items import JobboleItem, CommonItemLoader


class JobboleSpider(scrapy.Spider):
    name = 'jobbole'
    allowed_domains = ['jobbole.com']
    start_urls = ['http://python.jobbole.com/all-posts/']

    def parse(self, response: HtmlResponse):
        for i in range(1, 85):
            next_url = f'{self.start_urls[0]}/page/{i}/'
            yield Request(url=next_url, callback=self.parse_page)

    def parse_page(self, response: HtmlResponse):
        root_tag = '#archive .floated-thumb'
        thumb_list: List[Selector] = response.css(root_tag)
        thumb_count = len(thumb_list)
        for i in range(1, thumb_count + 1):
            current_thumb = f'{root_tag}:nth-child({i}) '
            item_loader = CommonItemLoader(item=JobboleItem(), response=response)
            item_loader.add_css('title', f'{current_thumb}.archive-title::text')
            item_loader.add_css('url', f'{current_thumb}.archive-title::attr(href)')
            item_loader.add_css('publish_date', f'{current_thumb}.post-meta >p:nth-child(1)')
            item_loader.add_css('category', f'{current_thumb}a[rel="category tag"]::text')
            item = item_loader.load_item()
            yield item

4. 保存爬取结果

下面要把爬去的结果保存到MongoDB中。我们要添加依赖包:

pip install pymongo

4.1 pipelines

在pipelines.py文件中添加数据库保存逻辑:

from pymongo import MongoClient
import settings


class MongoPipeline:
    def __init__(self):
        host = settings.MONGODB_HOST
        port = settings.MONGODB_PORT
        db_name = settings.DB_NAME
        client: MongoClient = MongoClient(host=host, port=port)
        self.db = client[db_name]

    def process_item(self, item, spider):
        sheet_name = item.__class__.__name__
        post = self.db[sheet_name]
        post.insert(dict(item))
        return item

4.2 在settings.py中添加配置

MONGODB_HOST = '192.168.3.12'
MONGODB_PORT = 27017
DB_NAME = 'article'

ITEM_PIPELINES = {
   'scrapy_demo.pipelines.MongoPipeline': 300,
}

到此,我们就可以通过以下命令启动爬虫了:

scrapy crawl jobbole

5. 动态网页爬取

动态网页是指使用javascript来动态加载内容的页面。比如说,使用前端框架制作的网站,在最开始的时候是没有任何HTML内容的。而爬虫只能爬取HTML页面,所以如果以普通的方式爬取动态网页的时候,是没有办法爬到任何内容的。
此时,我们需要使用selenium来爬取动态网页。

首先,我们需要添加依赖包:

pip install selenium

然后,我们需要下载selenium的驱动。Chrome浏览器的下载地址如下:
http://chromedriver.chromium.org/downloads

5.1 改写spider代码

我们需要在jobbole.py中添加selenium的初始化代码

# -*- coding: utf-8 -*-
import scrapy
from scrapy.xlib.pydispatch import dispatcher
from scrapy import Request, Selector, signals
from scrapy.http import HtmlResponse
from typing import List
from selenium import webdriver
from items import JobboleItem, CommonItemLoader


class JobboleSpider(scrapy.Spider):
    name = 'jobbole'
    allowed_domains = ['jobbole.com']
    start_urls = ['http://python.jobbole.com/all-posts']

    def __init__(self):
        opt = webdriver.ChromeOptions()
        opt.add_argument('--headless')	# 添加该配置可以不显示浏览器
        driver_path = 'D:\\app\\chromedriver_win32\\chromedriver.exe'
        self.browser = webdriver.Chrome(executable_path=driver_path, options=opt)
        super(JobboleSpider, self).__init__()
        dispatcher.connect(self.spider_closed, signal=signals.spider_closed)

    def spider_closed(self):
        self.browser.quit()

    def parse(self, response: HtmlResponse):
        for i in range(1, 5):
            next_url = f'{self.start_urls[0]}/page/{i}/'
            yield Request(url=next_url, callback=self.parse_page)
            print(f'..............{next_url}..............')

    def parse_page(self, response: HtmlResponse):
        root_tag = '#archive .floated-thumb'
        thumb_list: List[Selector] = response.css(root_tag)
        thumb_count = len(thumb_list)
        for i in range(1, thumb_count + 1):
            current_thumb = f'{root_tag}:nth-child({i}) '
            item_loader = CommonItemLoader(item=JobboleItem(), response=response)
            item_loader.add_css('title', f'{current_thumb}.archive-title::text')
            item_loader.add_css('url', f'{current_thumb}.archive-title::attr(href)')
            item_loader.add_value('root_url', response.url)
            item_loader.add_css('publish_date', f'{current_thumb}.post-meta >p:nth-child(1)')
            item_loader.add_css('category', f'{current_thumb}a[rel="category tag"]::text')
            item = item_loader.load_item()
            yield item

5.2 添加中间件

middlewares.py中添加中间件如下:

class JsPageMiddleware:
    def process_request(self, request: Request, spider):
        driver: WebDriver = spider.browser
        driver.get(request.url)
        return HtmlResponse(url=driver.current_url, body=driver.page_source, encoding='utf-8', request=request)

5.3 配置settings文件

最后把自定义的中间件配置到settings文件中

DOWNLOADER_MIDDLEWARES = {
    'scrapy_demo.middlewares.JsPageMiddleware': 1
    ...
}

到此为止,我们就可以通过selenium来获取网页内容了。

6. 随机User-Agent

我们使用fake_useragent插件。

pip install fake-useragent

6.1 添加中间件

class RandomUserAgentMiddleware:
    def __init__(self, crawler: Crawler):
        self.ua_type = crawler.settings.get('RANDOM_UA_TYPE', 'random')
        self.ua: FakeUserAgent = UserAgent()

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

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

6.2 配置settings

RANDOM_UA_TYPE = 'random'

DOWNLOADER_MIDDLEWARES = {
    'scrapy_demo.middlewares.JsPageMiddleware': 1,
    'scrapy_demo.middlewares.RandomUserAgentMiddleware': 1000
}

如此即可。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值