Scrapy项目练习

中文文档:http://scrapy-chs.readthedocs.io/zh_CN/latest/intro/tutorial.html#intro-tutorial

官网文档:https://doc.scrapy.org/en/latest/

 项目需求:

爬取伯乐在线的最新文章里的文章,因为有下一页的按钮可以通过下一页的选项来获取所有文章

新建项目

scrapy startproject ArticleSpider

创建爬虫

cd ArticleSpider\
scrapy genspider  jobbole blog.jobbole.com

可以用以下命令启动爬虫(在ArticleSpider目录下)

scrapy crawl jobbole

用pycharm打开项目

jobbole.py就是我们写爬虫代码的地方,默认是下面创建好后,jobbole.py里面的内容这样的

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


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

    def parse(self, response):
        pass

其中start_urls就是我们要爬取的url。类JobbleSpider继承了scrapy.Spider我打开简单看一下这里面写了什么(ctrl+鼠标左键点击)

一眼就看到Spider类的下面这些方法

class Spider(object_ref):
    # 此处省略  
    def start_requests(self):
        cls = self.__class__
        if method_is_overridden(cls, Spider, 'make_requests_from_url'):
            warnings.warn(
                "Spider.make_requests_from_url method is deprecated; it "
                "won't be called in future Scrapy releases. Please "
                "override Spider.start_requests method instead (see %s.%s)." % (
                    cls.__module__, cls.__name__
                ),
            )
            for url in self.start_urls:
                yield self.make_requests_from_url(url)
        else:
            for url in self.start_urls:
                yield Request(url, dont_filter=True)

    def make_requests_from_url(self, url):
        """ This method is deprecated. """
        return Request(url, dont_filter=True)

    def parse(self, response):
        raise NotImplementedError
    ......    

可以知道,scrapy调用下载器为我们下载url的内容,之后把结果给了parse,我们需将爬虫代码写在parse下即可,后面再对整个流程做进行更详细的分析。

在项目目录下创建一个用于执行命令的文件main.py,如下

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

from scrapy.cmdline import execute
import sys
import os

BASE_DIR = os.path.dirname(os.path.abspath(__file__))  # 工程目录D:\ArticleSpider
sys.path.append(BASE_DIR)  # 设置scrapy命令在此目录下运行
execute(["scrapy", "crawl", "jobbole"])  # 配置执行爬虫的命令

在写爬虫之前,我们将会对将会用到的xpath语法做一下简单的介绍

 xpath使用路径表达式在xml和html中进行导航,获取其中的元素。

xpath包含标准函数库

xpath是一个w3c的标准,因此不管什么库支持xpath的时候,其语法是一致的,所以说她的应用也是非常广的,值得一学。

xpath语法

xpath的定位用法

其他(第三项说明里的title改成div)

 好了,开始写代码

改一下url,改成具体文章的url

start_urls = ['http://blog.jobbole.com/110287/']

在parse方法里写代码

先找文章标题

xpath可以通过鼠标复制(谷歌浏览器)

得到的路径是://*[@id="post-110287"]/div[1]/h1

    def parse(self, response):
        re_selector = response.xpath('//*[@id="post-110287"]/div[1]/h1')
        pass

在pass那里做一饿断点进入main.py里debug一下

要获取文本内容直接在xpath路径后面加/text()

re_selector = response.xpath('//*[@id="post-110287"]/div[1]/h1/text()')

我们上面的每一次调试都请求了一次url,请求的有点慢,是比较浪费时间的,实际上scrapy给我们提供了一个用于调试的scrapy shell

在命令行中输入(工程目录下)

 scrapy shell http://blog.jobbole.com/110287/

获取标题

title = response.xpath('//div[@class="entry-header"]/h1/text()').extract_first("")

获取创建时间

create_date = response.xpath("//p[@class='entry-meta-hide-on-mobile']/text()").extract()[0].strip().replace("·","").strip()

获取点赞数

praise_nums = response.xpath("//span[contains(@class, 'vote-post-up')]/h10/text()").extract()[0]

获取收藏和评论,这个有点不同,我们只要数字,不要文字

fav_nums = response.xpath("//span[contains(@class, 'bookmark-btn')]/text()").extract()[0]
match_re = re.match(".*?(\d+).*", fav_nums)
if match_re:
    fav_nums = match_re.group(1)

comment_nums = response.xpath("//a[@href='#article-comment']/span/text()").extract()[0]
match_re = re.match(".*?(\d+).*", comment_nums)
if match_re:
    comment_nums = match_re.group(1)

获得正文

content = response.xpath("//div[@class='entry']").extract()[0]

获得标签,这个也有点不同,我们要标签但是如果有评论,这个评论我们是不要的

tag_list = response.xpath("//p[@class='entry-meta-hide-on-mobile']/a/text()").extract()
tag_list = [element for element in tag_list if not element.strip().endswith("评论")]
tags = ",".join(tag_list)

最后写完就是下面这样(extract_first("")表示,如果提取不到列表里的第一个值就返回空,如用[0]提取不到会报错)

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


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

    def parse(self, response):

        # 提取文章的具体字段
        title = response.xpath('//div[@class="entry-header"]/h1/text()').extract_first("")
        create_date = response.xpath("//p[@class='entry-meta-hide-on-mobile']/text()").extract()[0].strip().replace("·",
                                                                                                                    "").strip()
        praise_nums = response.xpath("//span[contains(@class, 'vote-post-up')]/h10/text()").extract()[0]
        fav_nums = response.xpath("//span[contains(@class, 'bookmark-btn')]/text()").extract()[0]
        match_re = re.match(".*?(\d+).*", fav_nums)
        if match_re:
            fav_nums = match_re.group(1)

        comment_nums = response.xpath("//a[@href='#article-comment']/span/text()").extract()[0]
        match_re = re.match(".*?(\d+).*", comment_nums)
        if match_re:
            comment_nums = match_re.group(1)

        content = response.xpath("//div[@class='entry']").extract()[0]

        tag_list = response.xpath("//p[@class='entry-meta-hide-on-mobile']/a/text()").extract()
        tag_list = [element for element in tag_list if not element.strip().endswith("评论")]
        tags = ",".join(tag_list)
        pass

下面来认识一下css选择器

 上面的xpath的操作实际上都可以替换成css选择器

# 通过css选择器提取字段
front_image_url = response.meta.get("front_image_url", "")  # 文章封面图
title = response.css(".entry-header h1::text").extract()[0]
create_date = response.css("p.entry-meta-hide-on-mobile::text").extract()[0].strip().replace("·", "").strip()
praise_nums = response.css(".vote-post-up h10::text").extract()[0]
fav_nums = response.css(".bookmark-btn::text").extract()[0]
match_re = re.match(".*?(\d+).*", fav_nums)
if match_re:
    fav_nums = int(match_re.group(1))
else:
    fav_nums = 0

comment_nums = response.css("a[href='#article-comment'] span::text").extract()[0]
match_re = re.match(".*?(\d+).*", comment_nums)
if match_re:
    comment_nums = int(match_re.group(1))
else:
    comment_nums = 0

content = response.css("div.entry").extract()[0]

tag_list = response.css("p.entry-meta-hide-on-mobile a::text").extract()
tag_list = [element for element in tag_list if not element.strip().endswith("评论")]
tags = ",".join(tag_list)

我们可以debug一下,看看是否正确

下一步,分析

parse获得文章列表页的url交给scrapy下载并进行解析

获取下一页的url并并交给scrapy进行下载,下载完成后交给parse

 

posted @ 2018-01-17 09:46  0bug  阅读(529)  评论(0)    收藏  举报