Python爬虫-Scrapy框架(四)- 内置爬虫文件 - 4.1 访问二级链接

写在前面

经过之前的学习,整个爬虫项目已经可以完成最基本的爬取操作,并且可以将数据存储至数据库,这一部分从提取二级链接入手,更加深入的探究内置爬虫文件。

开始一个新的项目文件

考虑到科创项目的需要,目前需要完成的任务便是提供一个关键词,爬取通过该关键词搜索到的所有文章,这就不可避免的要涉及自动获取下一页的url地址,重新丢回调度器继续爬取这一核心操作,因此新建一个项目,同时也算是对之前完成的一系列操作的复习。
大致思路是以搜索Scrapy关键词进入的页面url作为整个项目start_urls初始地址,按照之前的思路,爬取搜索到的文章的链接,并且获得作者信息,一并存储至数据库。这里和之前的内容基本完全一致,无非就是增加了两个信息的合并,所以这里不再做详细说明,如果有问题同样欢迎发送至我的邮箱(sunzhihao_future@nuaa.edu.cn),可以一起交流。
这个新开始的项目现在已经上传至CSDN资源以及百度网盘,同时附上项目的主要文件内容。

链接:https://pan.baidu.com/s/1nbYtvxKs1pinRrcjkWP8sA
提取码:4blq

search.py_(爬虫文件)

import scrapy
from ..items import SearchScrapyItem


class SearchSpider(scrapy.Spider):
    name = "search"
    start_urls = ['https://so.csdn.net/so/search/s.do?q=Scrapy&t=blog&o=&s=&l=']

    def parse(self, response):
        print(response)
        search = SearchScrapyItem()
        title_list = response.xpath("/html/body/div[@class='main-container']"
                                    "/div[@class='con-l']/div[@class='search-list-con']"
                                    "/dl[@class='search-list J_search']"
                                    "/dt/div[@class='limit_width']/a[1]").extract()
        writer_list = response.xpath("/html/body/div[@class='main-container']"
                                      "/div[@class='con-l']/div[@class='search-list-con']"
                                      "/dl[@class='search-list J_search']"
                                      "/dd[@class='author-time']/span[@class='author']/a/text()").extract()
        for i,j in zip(title_list, writer_list):
            search['title'] = i
            search['writer'] = j
            yield search
            # print(i, ":", j)

pipelines.py_(管道文件)

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

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: http://doc.scrapy.org/en/latest/topics/item-pipeline.html
import sqlite3


class SearchScrapyPipeline(object):
    def open_spider(self, spider):
        self.con = sqlite3.connect("search.sqlite")
        self.cu = self.con.cursor()

    def process_item(self, item, spider):
        print(spider.name, 'pipelines')
        insert_sql = "insert into article (title, writer) values ('{}', '{}')".format(item['title'], item['writer'])
        print(insert_sql)
        self.cu.execute(insert_sql)
        self.con.commit()
        return item

    def spider_close(self, spider):
        self.con.close()

items.py_

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

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

import scrapy


class SearchScrapyItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    title = scrapy.Field()
    writer = scrapy.Field()
    # pass

访问二级链接

添加注释的习惯

从这一部分开始,感觉代码会开始变得复杂,因此想到添加注释的重要性,因此在这里提醒自己,也提醒大家能养成及时在繁杂位置添加注释的好习惯,这既是对自己写过的代码的一种回顾性的检验,同时也能在未来改进代码时减少很多工作量,除此之外,在别人阅读代码时,能够更加轻松的了解整个代码的思路。

获得完整的二级链接

在开头时已经说过,获得下一页的url地址是现在想要完成的关键任务,这里不禁会抛出几个问题,网页中“下一页”按钮背后隐藏的二级链接是什么?如何获得一个完整的“下一页”url地址?
首先来到整个项目开始的初始地址(https://so.csdn.net/so/search/s.do?q=Scrapy&t=blog&u= ),打开开发者工具(Chrome浏览器快捷键为ctrl + shift + I),如下图所示。
link
选择位于右侧分栏左上角的选择工具,可以直接定位到鼠标指示的元素,如下图所示。
link
因此可以得知,这个链接到下一页的按钮在标签a下,可以通过独有的类名搜索到这个指向下一页的按钮,进而获得有价值的链接,即其href值,这里为下述字符串。

?p=2&q=Scrapy&t=blog&domain=&o=&s=&u=&l=&rbg=0”

关闭开发者工具,点击该按钮,进入到搜索的下一页,得到完整的url地址如下:

https://so.csdn.net/so/search/s.do?p=2&q=Scrapy&t=blog&domain=&o=&s=&u=&l=&rbg=0

再将项目开始的初始地址拿来作比较:

https://so.csdn.net/so/search/s.do?q=Scrapy&t=blog&u=

通过观察比较,不难发现,它们拥有共同的host_name(主地址),而一个完整的导向下一页的链接正是由host_name追加上按钮元素的href值拼接而成,这里运用字符串的format拼接很容易实现,现在对search.py爬虫文件中的内容做以下改动

import scrapy
from ..items import SearchScrapyItem


class SearchSpider(scrapy.Spider):
    name = "search"
    start_urls = ['https://so.csdn.net/so/search/s.do?q=Scrapy&t=blog&o=&s=&l=']
    # 设置host_name为url的format拼接做准备
    host_name = 'https://so.csdn.net/so/search/s.do{}'

    def parse(self, response):
        print(response)
        search = SearchScrapyItem()

		# 此语句用来搜索特定标签下的特定类名的元素,最后一层@href指示该元素的href值
		# 返回的为列表,我们需要的为列表中的首元素
        next_links = response.xpath(".//a[@class='btn btn-xs btn-default btn-next']/@href").extract()
        # 这里做一个简单的判断,防止没有搜索到该元素但是程序仍然运行出现错误
        if len(next_links) > 0:
        	# 进行完整url的拼接
            next_link = self.host_name.format(next_links[0])
            print("-----next_link-----", next_link)
        else:
            pass

这里用到了通过id和class查询xpath,可能第一次看起来有些奇怪,附上一个详细介绍这一部分内容的文档,可以做进一步了解。
Xpath教程-通过ID和Class检索
接下来执行该爬虫,查看程序运行情况。

C:\Users\Lenovo\Desktop\search_scrapy>scrapy crawl search


<200 https://so.csdn.net/so/search/s.do?q=Scrapy&t=blog&o=&s=&l=>
-----next_link----- https://so.csdn.net/so/search/s.do?p=2&q=Scrapy&t=blog&domain=&o=&s=&u=&l=&rbg=0

程序没有报错,并且成功得到了我们需要的url地址。

抓取所有页面的数据

接下来结合之前已经完成的爬取数据的工作,完全可以实现爬取到所有搜索到的文章,思路如下:

回调callback
初始页面
爬取当前页面数据
获得下一页url地址
若不为末页则将url丢回调度器
若为末页则结束爬取

根据以上思路,可以很清晰的写出代码,对search.py爬虫文件中的内容做以下改动

import scrapy
from ..items import SearchScrapyItem


class SearchSpider(scrapy.Spider):
    name = "search"
    start_urls = ['https://so.csdn.net/so/search/s.do?q=Scrapy&t=blog&o=&s=&l=']
    # 设置host_name为url的format拼接做准备
    host_name = 'https://so.csdn.net/so/search/s.do{}'

    def parse(self, response):
        print(response)
        search = SearchScrapyItem()

        # 此语句用来搜索特定标签下的特定类名的元素,最后一层@href指示该元素的href值
        # 返回的为列表,我们需要的为列表中的首元素
        next_links = response.xpath(".//a[@class='btn btn-xs btn-default btn-next']/@href").extract()
        # 这里做一个简单的判断,防止没有搜索到该元素但是程序仍然运行出现错误
        if len(next_links) > 0:
            # 进行完整url的拼接
            next_link = self.host_name.format(next_links[0])
            print("-----next_link-----", next_link)
            # 进行回调
            yield scrapy.Request(next_link, callback=self.parse)
        else:
            pass

        title_list = response.xpath("/html/body/div[@class='main-container']"
                                    "/div[@class='con-l']/div[@class='search-list-con']"
                                    "/dl[@class='search-list J_search']"
                                    "/dt/div[@class='limit_width']/a[1]/@href").extract()
        writer_list = response.xpath("/html/body/div[@class='main-container']"
                                      "/div[@class='con-l']/div[@class='search-list-con']"
                                      "/dl[@class='search-list J_search']"
                                      "/dd[@class='author-time']/span[@class='author']/a/text()").extract()
        for i,j in zip(title_list, writer_list):
            search['title'] = i
            search['writer'] = j
            yield search

这里用到了yield,是一个生成器,起到返回的作用,但是不停止函数的执行,同时还有节约内存的优点。
接下来执行该爬虫,查看程序运行情况。

C:\Users\Lenovo\Desktop\search_scrapy>scrapy crawl search


2019-04-17 18:58:25 [scrapy.core.scraper] DEBUG: Scraped from <200 https://so.csdn.net/so/search/s.do?p=18&q=Scrapy&t=blog&domain=&o=&s=&u=&l=&rbg=0>
{‘title’: ‘https://blog.csdn.net/beyond_f/article/details/74298569’,
‘writer’: ‘beyond_f’}
search pipelines
insert into article (title, writer) values (‘https://blog.csdn.net/qq_40390825/article/details/82715320’, ‘qq_40390825’)
2019-04-17 18:58:25 [scrapy.core.scraper] DEBUG: Scraped from <200 https://so.csdn.net/so/search/s.do?p=18&q=Scrapy&t=blog&domain=&o=&s=&u=&l=&rbg=0>
{‘title’: ‘https://blog.csdn.net/qq_40390825/article/details/82715320’,
‘writer’: ‘qq_40390825’}
2019-04-17 18:58:25 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://so.csdn.net/so/search/s.do?p=19&q=Scrapy&t=blog&domain=&o=&s=&u=&l=&rbg=0> (referer: https://so.csdn.net/so/search/s.do?p=18&q=Scrapy&t=blog&domain=&o=&s=&u=&l=&rbg=0)
<200 https://so.csdn.net/so/search/s.do?p=19&q=Scrapy&t=blog&domain=&o=&s=&u=&l=&rbg=0>
-----next_link----- https://so.csdn.net/so/search/s.do?p=20&q=Scrapy&t=blog&domain=&o=&s=&u=&l=&rbg=0
search pipelines
insert into article (title, writer) values (‘https://blog.csdn.net/weixin_33716941/article/details/86866447’, ‘weixin_33716941’)
2019-04-17 18:58:26 [scrapy.core.scraper] DEBUG: Scraped from <200 https://so.csdn.net/so/search/s.do?p=19&q=Scrapy&t=blog&domain=&o=&s=&u=&l=&rbg=0>
{‘title’: ‘https://blog.csdn.net/weixin_33716941/article/details/86866447’,
‘writer’: ‘weixin_33716941’}

执行的时间可能会比较长,执行结束后仔细观察会发现一共爬取到了20页,和网页里看到的是一致的,没有问题,我们打开数据库,可以发现爬取到的200条数据已经成功存储。
data
以上便是这部分的内容。

当前项目存档

从这一篇文章开始,以后每次完成的项目我都会保存一次,相当于记录自己的项目进展,同时也为以后万一需要归档做准备,大家也可以拿来做进一步的比较。
进行到这里的整个项目文件已经上传至我的百度网盘,如有需要可以自行下载。

版本:search_scrapy_v4.1
链接:https://pan.baidu.com/s/1Q1RpAZ5FwhbDE5QvvvFU0g
提取码:oyb4

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值