Scrapy实战8: Scrapy系统爬取伯乐在线

2020-02-12 11:16:11 浏览数 (1)

一、前言

上一篇给大家仔细讲解了如何用Xpath分类爬取医疗信息网站医疗器材名称和介绍图片,以及三种最常用的存储方法。

本篇是本系列的第八篇了,今天给大家讲讲如何用Scrapy系统爬取伯乐在线文章信息。

二、你不得不知道的 Knowledge

1.CSS选择器获取标签内容值和标签属性值
代码语言:javascript复制
eg. <a href = "https://blog.csdn.net/qq_39241986">极简XksA的博客</a>
# 1.获取标签里的内容值
response.css("a::text").extract()
极简XksA的博客
# 2.获取href属性值
response.css("a::attr(href)")
https://blog.csdn.net/qq_39241986
2.urllib包下的parse函数作用
代码语言:javascript复制
# python3下
from urllib import parse
url_main = "https://blog.csdn.net"
post_url = "/qq_39241986"
'''
urljoin函数参数介绍:
    url_main :第一个参数为主域名;
    post_url :第二个参数为需补全部分;
'''
url= parse.urljoin(url_main,post_url)
print(url)
# result :
#     https://blog.csdn.net/qq_39241986

通过代码容易看出,urllib包下的parse函数作用是能够补全我们获取的不完全链接(在上一篇中我们获取到的商品类别url就是不完全的url,当时是我们自己写方法修正的数据)

3.yield关键字介绍

一个带有 yield 的函数就是一个 generator(生成器),它和普通函数不同,生成一个 generator 看起来像函数调用,但不会执行任何函数代码,直到对其调用 next()(在 for 循环中会自动调用 next())才开始执行。虽然执行流程仍按函数的流程执行,但每执行到一个 yield 语句就会中断,并返回一个迭代值,下次执行时从 yield 的下一个语句继续执行。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。

yield 的好处是显而易见的,把一个函数改写为一个 generator 就获得了迭代能力,比起用类的实例保存状态来计算下一个 next() 的值,不仅代码简洁,而且执行流程异常清晰。

三、看代码,边学边敲边记Scrapy爬取伯乐在线

1.爬取逻辑思路分析

爬取逻辑思路

图上已经绘画和写的比较清楚了,一个简单思路就是:请求页面 -> 获取文章和下页链接 -> 获取文章详细信息 -> 翻页,再从头开始,一直到爬完所有页面,所有文章,下面我们具体动手试试。

2.调试方便,我们还是在cmd下用scrapy shell 进行调试
(1)获取主页面所有文章的url

1)页面分析:

页面分析文章url获取方法

通过图片上面标记,容易看出,我们要获取的文章url在 id 为archive的div下的class为post floated-thumb的div下的class为post-thumb的div下的a标签的href属性中,哈哈,读起来有点拗口,这里提供两种方法获取相应内容:

Xpath路径:

代码语言:javascript复制
'//*[@id="archive"]/div/div[1]/a/@href'

CSS选择器:

代码语言:javascript复制
# 注:因为post floated-thumb这个类名中 post和floated-thumb中间有个空格,表示两个类名,
# 我们可以直接根据后面部分来查找到这个class。(attr用来取属性值)
"#archive .floated-thumb .post-thumb a::attr(href)"

2)shell下运行结果

代码语言:javascript复制
# 我选择的是Xpath获取,个人比较习惯
>>> response.xpath('//*[@id="archive"]/div/div[1]/a/@href').extract()
['http://blog.jobbole.com/114334/', 'http://blog.jobbole.com/114331/', 'http://blog.jobbole.com/114329/', 'http://blog.jobbole.com/114327/', 'http://blog.jobbole.com/114324/', 'http://blog.jobbole.com/114321/', 'http://blog.jobbole.com/114319/', 'http://blog.jobbole.com/114311/', 'http://blog.jobbole.com/114308/', 'http://blog.jobbole.com/114303/', 
'http://blog.jobbole.com/114297/', 'http://blog.jobbole.com/114285/', 'http://blog.jobbole.com/114283/', 'http://blog.jobbole.com/114280/', 'http://blog.jobbole.com/114276/', 'http://blog.jobbole.com/114273/', 'http://blog.jobbole.com/114270/', 'http://blog.jobbole.com/114268/', 'http://blog.jobbole.com/114261/', 'http://blog.jobbole.com/114168/']
(2)获取翻页链接

1)页面分析:

页面分析获取翻页链接url

通过图片上面标记,容易看出,我们要获取的翻页url在class为next page-numbers的a标签的href属性中,中这里提供两种方法获取相应内容: Xpath路径:

代码语言:javascript复制
'//*[@id="archive"]/div[21]/a[4]/@href'

CSS选择器:

代码语言:javascript复制
# 页面上查找发现,next 属性值是唯一的,
# 所以可以直接根据类名next来查找下一页的url。
".next::attr(href)"

2)shell下运行结果

代码语言:javascript复制
# 我选择的是CSS选择器获取,一眼看出比较简单嘛
>>> response.css(".next::attr(href)").extract()[]
'http://blog.jobbole.com/all-posts/page/2/'
3.在Pycharm下实操代码
(1)基础代码
代码语言:javascript复制
# -*- coding: utf-8 -*-
import scrapy
import re
# 发送请求爬取页面
from scrapy.http import Request
# 归正url
from urllib import parse
# 爬虫类
class JobboleSpider(scrapy.Spider):
    name = 'jobbole'
    allowed_domains = ['blog.jobbole.com']
    start_urls = ['http://blog.jobbole.com/all-posts/']

(2)获取文章url下载和翻页下载代码
代码语言:javascript复制
# 爬取页面所有文章url和翻页url
# 翻页下载
def parse(self, response):
    # 1.获取单页面文章url
    post_urls = response.xpath('//*[@id="archive"]/div/div[1]/a/@href').extract()
    # 2.下载文章url
    for post_url in post_urls:
        yield Request(url= parse.urljoin(response.url,post_url),callback= self.parse_detail)
    # 3.获取翻页url和翻页下载
    next_url = response.css(".next::attr(href)").extract()
    if next_url != []:
        next_url = next_url[]
        yield Request(url= parse.urljoin(response.url,next_url),callback= self.parse)
(3)解析单篇文章信息
代码语言:javascript复制
# 获取单篇文章详情信息
def parse_detail(self,response):
    # 文章标题
    title = response.css(".entry-header h1 ::text").extract()[]

    # 发布日期
    data_r = response.css(".entry-meta-hide-on-mobile::text").extract()[].strip()
    data_time = data_r.replace('·','').strip()

    # 文章分类
    article_type = response.css("p.entry-meta-hide-on-mobile a::text").extract()
    if article_type != []:
        article_type = ",".join(article_type)

    # 点赞数
    praise_number = response.css(".href-style.vote-post-up h10::text").extract()
    if praise_number != [] :
        praise_number = int(praise_number[])
    else:
        praise_number = 

    # 收藏数
    collection_str = response.css("span.btn-bluet-bigger:nth-child(2)::text").extract()[]
    reg_02 = '.*?(d ).*'
    collection_number = re.findall(reg_02, collection_str)
    if collection_number:
        collection_number = int(collection_number[])
    else:
        collection_number = 

    # 评论数
    comment_number = response.css("a[href='#article-comment'] span::text").extract()[]
    comment_number = re.findall(reg_02, comment_number)
    if comment_number:
        comment_number = int(comment_number[])
    else:
        comment_number = 

    print("文章标题:" title)
    print("发布日期:" data_time)
    print("文章分类:" article_type)
    print("点赞数:" str(praise_number))
    print("收藏数:" str(collection_number))
    print("评论数:" str(comment_number))
    print("----------------------------------------")
(4)整合上面(1)、(2)、(3)后在pycharm下运行效果

我运行了差不多7-9秒,目测爬取了100条信息应该有,所以在爬取速度和可靠性上,依靠框架爬取要比自己request好的多嘿。

运行部分截图

代码语言:javascript复制
文章标题:泪流满面的  个 Git 面试题
发布日期://
文章分类:IT技术,Git
点赞数:
收藏数:
评论数:
----------------------------------------
文章标题:设计微服务的最佳实践
发布日期://
文章分类:IT技术,微服务
点赞数:
收藏数:
评论数:
----------------------------------------
···
文章标题:如何自动唤醒和关闭 Linux
发布日期://
文章分类:IT技术,Linux
点赞数:
收藏数:
评论数:
----------------------------------------
文章标题:他们是优秀的前端,所以这些后端工作也交给他们…
发布日期://
文章分类:职场,程序员
点赞数:
收藏数:
评论数:
----------------------------------------

四、后言

通过本次学习,不知道大家有没有对Scrapy有多一点点了解嘿,通过本次学习我知道了如何把页面发送给Scrapy,让它帮忙下载,即使是几千条数据,也没有出现连接错误,同时知道了关键字yield的基本使用方法,我觉得最重要的是我们爬取的思路,以及在爬取过程中如何选取更加适合的匹配方法(目前我们已经讲了:正则、Xpath、CSS选择器)。

继续加油,下一节我们将讲解如何设计数据库来存储我们获取的数据,并利用items方法交给pipelines进行数据存储和查重。

【完】

0 人点赞