一文搞定scrapy爬取众多知名技术博客文章保存到本地数据库,包含:cnblog、csdn、51cto、itpub、jobbole、oschina等...

本文旨在通过爬取一系列博客网站技术文章的实践,介绍一下scrapy这个python语言中强大的整站爬虫框架的使用。各位童鞋可不要用来干坏事哦,这些技术博客平台也是为了让我们大家更方便的交流、学习、提高的,大家千万要珍惜哦(-_-)。

本文目录:

  • 0、开发环境
  • 1、目标介绍
  • 2、爬取目标
    • 2.1、csdn博客
      • 2.1.1 如何判断是否为Ajax方式异步获取的?
      • 2.1.2 爬虫实现
        • 2.1.2.1 修改spider接口实现
        • 2.1.2.2 修改items.py文件
        • 2.1.2.3 修改pipeline.py文件
        • 2.1.2.4 修改settings.py文件
        • 2.1.2.5 修改dbhelper.py文件
      • 2.1.3 启动爬虫
      • 2.1.4 源码下载
    • 2.2、cnblog博客园
      • 2.2.1、页面分析
      • 2.2.2、爬虫实现
        • 2.2.2.1、创建项目
        • 2.2.2.2、spider实现
        • 2.2.2.3、items实现
        • 2.2.2.4、pipeline、settings、dbhelper的实现
      • 2.2.3、启动爬虫
      • 2.2.4、源码下载
    • 2.3、51cto博客
      • 2.3.1 页面分析
      • 2.3.2、启动爬虫
      • 2.3.3、源码下载
    • 2.4、jobbole伯乐在线
      • 2.4.1 页面分析
      • 2.4.2、启动爬虫
      • 2.4.3、源码下载
    • 2.5、itpub博客(待续)
      • 2.5.1、页面分析
      • 2.5.2、启动爬虫
      • 2.5.3、源码下载
    • 2.6、oschina开源中国博客
      • 2.6.1、页面分析
      • 2.6.2、爬虫实现
      • 2.6.3、启动爬虫
      • 2.6.4、源码下载
  • 3、参考资料

0、开发环境

本文环境:
Win7 64位
Python 版本:3.6.5
pip 版本:18.1
scrapy 版本:1.5
pymysql 版本:0.9.2
Visual Code 版本: 1.28.2
Mysql 5.7 (不会安装的自行百度,这里就不岔话题了)

至于初始化Visual Code(后续文中统一简称:vscode)的Python编程环境,请参考博文:Python3从零开始爬取今日头条的新闻【一、开发环境搭建】
建议通过Anaconda3 来搭建python开发环境,省心。

【注】:文中源码有关XPath解析页面内容提取文章元素的解读,本文不再啰嗦重复了,可以参考前面的博文:
Python3从零开始爬取今日头条的新闻【一、开发环境搭建】
Python3从零开始爬取今日头条的新闻【二、首页热点新闻抓取】(2.1.3节有介绍XPath使用)

1、目标介绍

今天的目标网站主要分为两类:

  • 一类是页面通过Ajax异步请求的方式获取翻页文章列表

    比如强大的csdn博客、oschina开源中国就是属于第一类通过Ajax异步加载的方式获取更多内容的

  • 另一类是直接通过界面的上一页、下一页、跳转到第几页的方式直接加载下一页的文章列表

    比如cnblog博客园、51cto、iteye、itpub、jobbole伯乐在线等几个是直接通过页面导航翻页的。

下面让我们来一个一个分析、实现爬取技术文章保存到本地。

2、爬取目标

2.1、csdn博客

2.1.1 如何判断是否为Ajax方式异步获取的?

有很多种方式,比如,我们打开火狐浏览器(其它浏览器也一样),按F12打开浏览器的调试模式,选中【网络】,点击【XHR】来过滤异步请求,
一文搞定scrapy爬取众多知名技术博客文章保存到本地数据库,包含:cnblog、csdn、51cto、itpub、jobbole、oschina等..._第1张图片

然后输入csdn的网址:https://www.csdn.net/,我们看到列表有很多异步的请求,别慌,我们找一下很容易发现其中一个返回json格式的传输数据大小28KB的是我们的目标,点开这个请求发现右边返回的确实是文章列表:
一文搞定scrapy爬取众多知名技术博客文章保存到本地数据库,包含:cnblog、csdn、51cto、itpub、jobbole、oschina等..._第2张图片

我们通过关键字articles过滤一下这个请求,再往下滑动发现又发了同样的请求,不过参数不一样:
一文搞定scrapy爬取众多知名技术博客文章保存到本地数据库,包含:cnblog、csdn、51cto、itpub、jobbole、oschina等..._第3张图片

分析下这几个网址:
https://www.csdn.net/api/articles?type=more&category=home&shown_offset=1541633731306080
https://www.csdn.net/api/articles?type=more&category=home&shown_offset=1541579172175664
https://www.csdn.net/api/articles?type=more&category=home&shown_offset=1541572185652051

发现其中不一样的就是最后一个shown_offset参数值,这个看起来像时间戳,但是是什么时间戳呢?当前系统时间?上次返回数据的最后一个时间戳?让我们看下响应里面的数据:
一文搞定scrapy爬取众多知名技术博客文章保存到本地数据库,包含:cnblog、csdn、51cto、itpub、jobbole、oschina等..._第4张图片分析了下可以知道,第二个请求的这个时间戳,正是第一个请求返回数据的最后一个里面的这个值,我们再分析后面几个请求发现都是符合这个规律的,OK了,那接下来就是写爬虫去获取数据了。

2.1.2 爬虫实现

打开vscode终端定位到工程目录,例如d:/tmp/csdnnews
输入:
scrapy startproject csdnnews
提示成功后,进入到csdnnews目录
cd csdnnews
然后输入以下命令创建爬虫:
scrapy genspider csdnspider www.csdn.net
其中csdnspider 是爬虫类的名字,www.csdn.net 是我们要爬虫允许的域名地址
此时目录如下(db目录是后面加的数据库操作工具类的文件夹)
一文搞定scrapy爬取众多知名技术博客文章保存到本地数据库,包含:cnblog、csdn、51cto、itpub、jobbole、oschina等..._第5张图片

这里面我们要改几个地方来实现我们的功能。

2.1.2.1 修改spider接口实现

主要修改的内容有:
url:开始爬虫的首页
allow_domians:允许爬虫的网站域名
def parse(self, response):这里处理爬虫返回的网页内容,抓取数据
第一次的url中要拼接一个16位的时间戳,类中提供了方法,后续的这个offset都是根据上一次返回的数据最后一个里面的offset来赋值即可。
这个spider类的主要内容如下:

class tbmmSpider(Spider):
    # url = "https://www.csdn.net/api/articles?type=more&category=home&shown_offset="
    url = 'https://www.csdn.net/api/articles?type=more&category=newarticles&shown_offset='
    name = "csdnspider"
    allow_domians = ["www.csdn.net"]
    
    def get_time_stamp16(self):
        # 生成16时间戳   eg:1540281250399895    -ln
        datetime_now = datetime.datetime.now()
       
        # 10位,时间点相当于从1.1开始的当年时间编号
        date_stamp = str(int(time.mktime(datetime_now.timetuple())))

        # 6位,微秒
        data_microsecond = str("%06d"%datetime_now.microsecond)

        date_stamp = date_stamp+data_microsecond
        return int(date_stamp)

    def start_requests(self):
        curl = self.url + str(self.get_time_stamp16())
        yield Request(curl, headers=self.headers)    

    def parse(self, response):
        datas = json.dumps(response.text, ensure_ascii= False, indent=4, separators=(',', ': '))
       
        json_data = json.loads(datas)
        json_data = json_data.replace('null', '\"\"').replace(u'None', u'\"\"').replace(u'false', 'False').replace(u'true', 'True')
       
        dict = eval(json_data)
        articles = dict['articles']
        
        if articles and len (articles) > 0:
            for article in articles:
                item = CsdnnewsItem()
                item['avatar'] = article['avatar']
                item['title'] = article['title']
                item['category'] = article['category']
                item['category_id'] = article['category_id']
                item['channel'] = article['channel']
                item['show_datetime'] = article['created_at']
                item['cur_id'] = article['id']
                item['user_name'] = article['user_name']
                item['nickname'] = article['nickname']
                item['user_url'] = article['user_url']
                item['showtime'] = article['shown_time']
                item['source_from'] = 'csdn'
                item['summary'] = article['summary']
                item['tag'] = article['tag']
                item['type'] = article['type']
                item['detail_url'] = article['url']
                item['views_count'] = article['views']
                item['comments_count'] = article['comments']
                shown_offset = article['shown_offset']
                yield item
        
            #如果datas存在数据则对下一页进行采集
            time.sleep(0.5)
            next_url = self.url + str(shown_offset)
            yield Request(next_url, headers=self.headers)    

2.1.2.2 修改items.py文件

声明接收抓取内容的item类属性
内容如下:


class CsdnnewsItem(scrapy.Item):
    # define the fields for your item here like:
    
    # 头像
    avatar = scrapy.Field()

    # 标题
    title = scrapy.Field()

    # 分类文本
    category = scrapy.Field()

    # 分类id
    category_id = scrapy.Field()

    # 渠道
    channel = scrapy.Field()

    # 创建时间
    created_time = scrapy.Field()

    # 当前id
    cur_id = scrapy.Field()

    # 用户名
    user_name = scrapy.Field()

    # 作者昵称
    nickname = scrapy.Field()

    # 用户详情url
    user_url = scrapy.Field()

    # 展示时间
    showtime = scrapy.Field()

    # 展示时间,比如2018年8月、19小时前、2天前
    show_datetime = scrapy.Field()
    
    # 来源
    source_from = scrapy.Field()
    
    # 摘要
    summary = scrapy.Field()

    # 标签(多个标签以|分割)
    tag = scrapy.Field()

    # 类型(blog、news、article)
    type = scrapy.Field()

    # 详情url
    detail_url = scrapy.Field()

    # 阅读数量
    views_count = scrapy.Field()

    # 评论数量
    comments_count = scrapy.Field()
    

2.1.2.3 修改pipeline.py文件

实现保存数据到mysql数据库

class CsdnnewsPipeline(object):
    def __init__(self):
        self.db = DBHelper()


    def process_item(self, item, spider):
        self.db.insert(item)

        return item

    def close_spider(self, spider):
       

2.1.2.4 修改settings.py文件

声明pymysql数据库连接信息等


BOT_NAME = 'csdnnews'

SPIDER_MODULES = ['csdnnews.spiders']
NEWSPIDER_MODULE = 'csdnnews.spiders'

#mysql-连接配置
MYSQL_HOST = '127.0.0.1'
MYSQL_DBNAME = 'voanews'
MYSQL_USER = 'news'
MYSQL_PASSWD ='123456'
MYSQL_PORT = 3306

# 下面这个要打开,否则无法通过pipe管道存储到数据库
ITEM_PIPELINES = {
    'csdnnews.pipelines.CsdnnewsPipeline': 300,
}

2.1.2.5 修改dbhelper.py文件

实现保存到数据库

# -*- coding: utf-8 -*-
import pymysql
from twisted.enterprise import adbapi
from scrapy.utils.project import get_project_settings  #导入seetings配置

class DBHelper():

    def __init__(self):
        settings = get_project_settings()  #获取settings配置,设置需要的信息

        dbparams = dict(
            host=settings['MYSQL_HOST'],  #读取settings中的配置
            db=settings['MYSQL_DBNAME'],
            user=settings['MYSQL_USER'],
            passwd=settings['MYSQL_PASSWD'],
            charset='utf8',  #编码要加上,否则可能出现中文乱码问题
            cursorclass=pymysql.cursors.DictCursor,
            use_unicode=False,
        )
        #**表示将字典扩展为关键字参数,相当于host=xxx,db=yyy....
        dbpool = adbapi.ConnectionPool('pymysql', **dbparams)
        self.__dbpool = dbpool

    def connect(self):
        return self.__dbpool

    #插入数据
    def insert(self, item):
        #这里定义要插入的字段
       
        sql = "insert into news(avatar, title, category, category_id, channel,show_datetime,\
         cur_id, user_name, nickname, user_url, showtime, source_from, summary, tag, type, detail_url, views_count, comments_count)\
          values(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)"
        #调用插入的方法
        query = self.__dbpool.runInteraction(self._conditional_insert, sql, item)
        #调用异常处理方法
        query.addErrback(self._handle_error)
        return item

    #写入数据库中
    def _conditional_insert(self, canshu, sql, item):
        
        #取出要存入的数据,这里item就是爬虫代码爬下来存入items内的数据
        params = (item['avatar'], item['title'], item['category'], item['category_id'], item['channel'], 
        item['show_datetime'], item['cur_id'], item['user_name'], item['nickname'], item['user_url'], 
        item['showtime'], item['source_from'], item['summary'], item['tag'], item['type'], item['detail_url'], item['views_count'], item['comments_count'])
        canshu.execute(sql, params)

    #错误处理方法
    def _handle_error(self, failue):
        print('--------------database operation exception!!-----------------')
        print(failue)

    def __del__(self):
        try:
            self.__dbpool.close()
        except Exception as ex:
            print(ex)
            

2.1.3 启动爬虫

在启动前,先初始化mysql的表结构,源码中db目录有个init.sql文件,执行后会创建一个存储爬取内容的表结构,同时要把setting中配置的用户名,在mysql中创建相应的用户以及授权给刚才新建的表。不清楚的可以评论回复,这里不岔话题了。
我们在刚才的spider目录的同级路径,输入以下命令即可启动爬虫:

scrapy crawl csdnspider

2.1.4 源码下载

本节结束,爬取csdn博文的源码下载:点我去下载完整源码


2.2、cnblog博客园

2.2.1、页面分析

我们打开博客园的java分类,地址是:https://www.cnblogs.com/cate/java/
可以看到这个网站跟前面的csdn不一样的是,这个的文章列表内容是直接在源码里面的,这种是最容易爬取的,我们在页面右键查看源码内容如下:
一文搞定scrapy爬取众多知名技术博客文章保存到本地数据库,包含:cnblog、csdn、51cto、itpub、jobbole、oschina等..._第6张图片

刚好对应了页面的前两篇文章:

一文搞定scrapy爬取众多知名技术博客文章保存到本地数据库,包含:cnblog、csdn、51cto、itpub、jobbole、oschina等..._第7张图片

下面让我们来看下页面的文章列表布局:
一文搞定scrapy爬取众多知名技术博客文章保存到本地数据库,包含:cnblog、csdn、51cto、itpub、jobbole、oschina等..._第8张图片

所有的文章都是在一个id值为post_listdiv里面,每一个文章布局都是包裹在一个class属性值为post_itemdiv布局里面。

我们再看下底部的翻页跳转导航内容:

一文搞定scrapy爬取众多知名技术博客文章保存到本地数据库,包含:cnblog、csdn、51cto、itpub、jobbole、oschina等..._第9张图片

翻页导航布局是在一个class属性值为pagerdiv里面,每一个a标签表示一页导航地址。

找到页面的规律后,接下来让我们编写爬虫实现文章的提取。

2.2.2、爬虫实现

2.2.2.1、创建项目

打开vscode终端定位到工程目录,例如d:/tmp/cnblog
输入:
scrapy startproject cnblog
提示成功后,进入到cnblog目录
cd cnblog
然后输入以下命令创建爬虫:
scrapy genspider -t crawl cnblogspider www.cnblogs.com
其中cnblogspider 是爬虫类的名字,www.cnblogs.com 是我们要爬虫允许的域名地址
此时目录如下(db目录是后面加的数据库操作工具类的文件夹)。

这里跟前面一节实现爬取csdn博文的爬虫不一样的是,在创建爬虫的时候,加了参数-t crawl,就是以crawl模板来创建一个继承自CrawlSpiderspider

2.2.2.2、spider实现

spider的内容如下:
其中start_urls 是代表要爬取的首页列表,这里选择了java、python、linux等分类;
parse_item是我们处理数据的回调,这里解析页面提取文章各元素;
rules是告诉爬虫应该如何提取翻页导航,这里是以xpath方式定位翻页导航元素,根据2.2.2.1节里面的页面分析很容易理解。


class CnblogspiderSpider(CrawlSpider):
    name = 'cnblogspider'
    allowed_domains = ['www.cnblogs.com']
    start_urls = [
        'https://www.cnblogs.com/cate/java/',
        'https://www.cnblogs.com/cate/python/',
        'https://www.cnblogs.com/cate/job/',
        'https://www.cnblogs.com/cate/algorithm/',
        'https://www.cnblogs.com/cate/linux/',
        'https://www.cnblogs.com/cate/mysql/',
        'https://www.cnblogs.com/cate/cpp/',
        'https://www.cnblogs.com/cate/go/'
        ]

    rules = (
        Rule(LinkExtractor(restrict_xpaths=('//div[@id="pager_bottom"]/div[@id="paging_block"]/div[@class="pager"]/a', )),  callback='parse_item', follow=True),
    )

    
    def parse_item(self, response):
        for article in response.xpath('//div[@id="post_list"]/div[@class="post_item"]'):
            item = CnblogItem()
            try:
                
                item['title'] = article.xpath('./div[@class="post_item_body"]/h3/a[@class="titlelnk"]/text()').extract_first()
                item['summary'] = article.xpath('./div[@class="post_item_body"]/p[@class="post_item_summary"]/text()').extract_first()
                item['detail_url'] = article.xpath('./div[@class="post_item_body"]/h3/a[@class="titlelnk"]/@href').extract_first()
                item['logo_url'] = article.xpath('./div[@class="post_item_body"]/p[@class="post_item_summary"]/a/img/@src').extract_first()
                
                item['source_from'] = '博客园'
                item['show_datetime'] = article.xpath('./div[@class="post_item_body"]/p/div[@class="post_item_foot"]/text()').extract_first()
                item['user_name'] = article.xpath('./div[@class="post_item_body"]/p/div[@class="post_item_foot"]/a/text()').extract_first()
                item['nickname'] = article.xpath('./div[@class="post_item_body"]/p/div[@class="post_item_foot"]/a/text()').extract_first()
                item['user_url'] = article.xpath('./div[@class="post_item_body"]/p[@class="post_item_summary"]/a/@href').extract_first()
                item['cur_id'] = ''

                item['views_count'] =  article.xpath('./div[@class="post_item_body"]/div[@class="post_item_foot"]/span[@class="article_view"]/a/text()').extract_first()
                item['views_count'] = re.findall("\((\d*?)\)", item['views_count'])[0]
                item['comments_count'] =  article.xpath('./div[@class="post_item_body"]/div[@class="post_item_foot"]/span[@class="article_comment"]/a/text()').extract_first()
                item['comments_count'] = re.findall("\((\d*?)\)", item['comments_count'])[0]
                print(item['views_count'])
                print(item['comments_count'])
            except Exception as ex:
                print(ex)
           
            yield item

2.2.2.3、items实现

跟前面2.1节爬取csdn的一样,定义了一样的元素,只不过不同的博客文章,所能提取的字段不一样,只是这里定义的一部分而已。这里就不重复贴代码了。

2.2.2.4、pipeline、settings、dbhelper的实现

这几个都跟2.1节爬取csdn的差不多,只是在dblhelper里面存储的字段不一样而已,具体直接看源码吧。

2.2.3、启动爬虫

如果已经按照2.1爬取csdn的章节里面初始化过mysql数据库了,这一步可以省略。

在启动前,先初始化mysql的表结构,源码中db目录有个init.sql文件,执行后会创建一个存储爬取内容的表结构,同时要把setting中配置的用户名,在mysql中创建相应的用户以及授权给刚才新建的表。不清楚的可以评论回复,这里不岔话题了。

我们在刚才的spider目录的同级路径,输入以下命令即可启动爬虫:

scrapy crawl cnblogspider

2.2.4、源码下载

本节结束,爬取cnblog博客园文章的源码下载:点我去下载完整源码


2.3、51cto博客

2.3.1 页面分析

我们打开技术类的推荐分类:http://blog.51cto.com/artcommend,在页面右键查看源码,可以发现文章列表标题在源码中,这种跟前面2.2节的博客园网站的爬取一样的。
一文搞定scrapy爬取众多知名技术博客文章保存到本地数据库,包含:cnblog、csdn、51cto、itpub、jobbole、oschina等..._第10张图片

2.3.2、启动爬虫

如果已经按照2.1爬取csdn的章节里面初始化过mysql数据库了,这一步可以省略。

在启动前,先初始化mysql的表结构,源码中db目录有个init.sql文件,执行后会创建一个存储爬取内容的表结构,同时要把setting中配置的用户名,在mysql中创建相应的用户以及授权给刚才新建的表。不清楚的可以评论回复,这里不岔话题了。

我们在刚才的spider目录的同级路径,输入以下命令即可启动爬虫:

scrapy crawl ctospider

2.3.3、源码下载

本节结束,爬取51CTO博客文章的源码下载:点我去下载完整源码


2.4、jobbole伯乐在线

2.4.1 页面分析

我们打开伯乐在线的全部文章页面:http://blog.jobbole.com/all-posts/
可以看到底部有分页导航,是不是很爽…克制一点,以学习为目的,不要耍流氓哈,不要干坏事。
一文搞定scrapy爬取众多知名技术博客文章保存到本地数据库,包含:cnblog、csdn、51cto、itpub、jobbole、oschina等..._第11张图片
创建工程、创建爬虫、编写spider等就不重复了,跟前面的一样一样的,直接看源码会更直接。

2.4.2、启动爬虫

如果已经按照2.1爬取csdn的章节里面初始化过mysql数据库了,这一步可以省略。

在启动前,先初始化mysql的表结构,源码中db目录有个init.sql文件,执行后会创建一个存储爬取内容的表结构,同时要把setting中配置的用户名,在mysql中创建相应的用户以及授权给刚才新建的表。不清楚的可以评论回复,这里不岔话题了。

我们在刚才的spider目录的同级路径,输入以下命令即可启动爬虫:

scrapy crawl jobbolespider

2.4.3、源码下载

本节结束,爬取伯乐在线博客文章的源码下载:点我去下载完整源码


2.5、itpub博客(待续)

2.5.1、页面分析

我们打开itpub的linux专题:http://blog.itpub.net/linux/
一文搞定scrapy爬取众多知名技术博客文章保存到本地数据库,包含:cnblog、csdn、51cto、itpub、jobbole、oschina等..._第12张图片
我们右键查看源码,可以看到文章内容就在源码中,只不过这个网站的翻页导航不是跟前面的一样有直接的第1页、第2页、下一页的形式,而是在底部有个“点击加载更多”的布局,其实也是一个a标签,跟其它的分页导航没有本质的区别。

创建工程、创建爬虫、编写spider等就不重复了,跟前面的一样一样的,直接看源码会更直接。

2.5.2、启动爬虫

如果已经按照2.1爬取csdn的章节里面初始化过mysql数据库了,这一步可以省略。

在启动前,先初始化mysql的表结构,源码中db目录有个init.sql文件,执行后会创建一个存储爬取内容的表结构,同时要把setting中配置的用户名,在mysql中创建相应的用户以及授权给刚才新建的表。不清楚的可以评论回复,这里不岔话题了。

我们在刚才的spider目录的同级路径,输入以下命令即可启动爬虫:

scrapy crawl itpubspider

2.5.3、源码下载

本节结束,爬取ITPUB博客文章的源码下载:点我去下载完整源码


2.6、oschina开源中国博客

2.6.1、页面分析

开源中国的博客就比较有意思,他的翻页比较隐藏,但是还是可以分析得到的。我们打开编程语言的首页,并且按F12打开调试模式查看Ajax过滤异步请求:
一文搞定scrapy爬取众多知名技术博客文章保存到本地数据库,包含:cnblog、csdn、51cto、itpub、jobbole、oschina等..._第13张图片
可以看到这里是有异步请求获取内容的,并且从过滤的Ajax请求的内容一看便知,在当前分类(比如截图中的编程语言分类是classification=428609)定了的前提下,p=2就是表示获取第二页的数据了,type=ajax表示以异步的方式返回文章列表。

再让我们看下源码,在当前页面右键查看源码,如下图:可以看到它的内容在源码里面的:
一文搞定scrapy爬取众多知名技术博客文章保存到本地数据库,包含:cnblog、csdn、51cto、itpub、jobbole、oschina等..._第14张图片

但是它页面上又看不到上一页、下一页、或者第1页,第2页之类的分页导航的布局。从下图可以看到,其实是有的,只不过它的布局元素都设置了显示属性为隐藏的:style="display: none",所以我们看不到。
这样我们就知道在crawl爬虫中如何获取爬虫所需下一页的布局元素了。
一文搞定scrapy爬取众多知名技术博客文章保存到本地数据库,包含:cnblog、csdn、51cto、itpub、jobbole、oschina等..._第15张图片

那还有个问题:oschina开源中国这个网站的博客页面,跟前面的几个可以通过页面翻页导航自动爬取同系列的博客页面不一样的是,这里没有告诉我们它的最后一页是多少,如何判断呢?
我们这里采取一个策略:如果当前这次请求,没有返回任何博客文章内容了,就认为到最后一页了。

2.6.2、爬虫实现

ok,前面分析过页面结构、翻页导航定位、判断最后一页的策略了,这里就具体实现爬虫模块。

这里我们把爬虫访问的首页定为p=1,链接就是前面2.6.1分析的Ajax异步请求里面的内容,只是把p=2改为p=1就是访问第一页了。
下一页的定位规则也很容易理解,通过XPath定位到2.6.1分析的那个隐藏的翻页的a标签即可。

start_urls = ['https://www.oschina.net/blog/widgets/_blog_index_recommend_list?classification=0&type=ajax&p=1']

rules = (
    Rule(LinkExtractor(restrict_xpaths=('//p[@class="pagination"]/a[contains(@class, "pagination__next")]', )),  callback='parse_item', follow=True),
)

解析文字列表的方法如下:这个直接看代码就行了,主要是根据XPath提取我们需要的字段,如果对XPath不熟悉,可以参考我的另一篇文章有详细介绍使用示例:
Python3从零开始爬取今日头条的新闻【二、首页热点新闻抓取】
2.1.3节有介绍。

def parse_item(self, response):
    for article in response.xpath('//div/div[contains(@class, "blog-item")]'):
        item = OschinaItem()
        try:
            
            item['title'] = article.xpath('./div[@class="content"]/a[@class="header"]/@title').extract_first()
            item['summary'] = article.xpath('./div[@class="content"]/div[@class="description"]/p/text()').extract_first()
            item['detail_url'] = article.xpath('./div[@class="content"]/a[@class="header"]/@href').extract_first()
            item['source_from'] = '开源中国'
            item['show_datetime'] = article.xpath('./div[@class="content"]/div[@class="extra"]/div/div[2]/text()').extract_first()
            item['user_name'] = article.xpath('./div[@class="content"]/div[@class="extra"]/div/div[1]/a/text()').extract_first()
            item['nickname'] = article.xpath('./div[@class="content"]/div[@class="extra"]/div/div[1]/a/text()').extract_first()
            item['user_url'] = article.xpath('./div[@class="content"]/div[@class="extra"]/div/div[1]/a/@href').extract_first()
            item['cur_id'] = article.xpath('./@data-id').extract_first()

            item['views_count'] = article.xpath('./div[@class="content"]/div[@class="extra"]/div/div[3]/text()').extract_first()
            view_count_str = str(item['views_count'])

            # 因为页面上的浏览数,这个网站返回的是2k,100之类的,要统一转为整型存储到数据库
            if view_count_str.find('K') > -1:
                view_count_str = view_count_str.lstrip().rstrip()   
                item['views_count'] = int(float(view_count_str.replace('K',''))*1000.0)
            if view_count_str.find('w') > -1:
                item['views_count'] = str(float(view_count_str.replace('w',''))*10000.0)
            
            item['comments_count'] = article.xpath('./div[@class="content"]/div[@class="extra"]/div/div[4]/a/text()').extract_first()
            
        except Exception as ex:
            print(ex)
       
        yield item

OK,主要的就上面这个代码,其它的跟前面章节的都大同小异,直接看源码即可。

2.6.3、启动爬虫

如果已经按照2.1爬取csdn的章节里面初始化过mysql数据库了,这一步可以省略。

在启动前,先初始化mysql的表结构,源码中db目录有个init.sql文件,执行后会创建一个存储爬取内容的表结构,同时要把setting中配置的用户名,在mysql中创建相应的用户以及授权给刚才新建的表。不清楚的可以评论回复,这里不岔话题了。

我们在spider目录的同级路径,输入以下命令即可启动爬虫:

scrapy crawl oschinaspider

2.6.4、源码下载

本节结束,爬取OSChina开源中国博客文章的源码下载:点我去下载完整源码


全部内容完毕,这里只是总结下自己最近学习scrapy爬虫的几个实践,通过这几个实践,一般的类似需求应该都可以搞定,当然这里没有涉及到反爬虫厉害的站点的处理,比如浏览器header伪装、ip代理、爬虫时间间隔等,后续有机会再实践下。

本文内容如有错误,恳请斧正,如有更好的技术,欢迎指点一二,谢谢。


3、参考资料

[1]: XPath语法参考
[2]: 廖雪峰老师的Python3 在线学习手册
[3]: Python3官方文档
[4]: 菜鸟学堂-Python3在线学习
[5]: scrapy官方文档
[6]: scrapy中文网

转载于:https://www.cnblogs.com/xiaocy66/p/10589254.html

你可能感兴趣的:(一文搞定scrapy爬取众多知名技术博客文章保存到本地数据库,包含:cnblog、csdn、51cto、itpub、jobbole、oschina等...)