基于Scrapy框架实现网络爬虫-Python

1. 网络爬虫原理

互联网上大量的信息以网页形式提供给用户,用户通过浏览器从服务器获得网页数据并经过浏览器解析后,进行网页阅读、内容复制、链接单击等操作。用户与网页服务器的通信是通过HTTP或者HTTPS实现的,网络浏览器是用户向服务器发送请求数据、接收服务器回应数据、解析并呈现服务器回应数据的客户端软件。
用户不通过浏览器而是通过程序自动获取网页内容,有两种办法:一是当服务器提供API方法时,可以调用API获取网页数据;二是当服务器没有提供API方法时,需要使用爬虫程序从服务器获取网页数据并从中过滤提取所需数据。

2. Scrapy框架

Scrapy是适用于Python的一个快速、高层次的屏幕抓取和web抓取框架,用于抓取web站点并从页面中提取结构化的数据。Scrapy用途广泛,可以用于数据挖掘、监测和自动化测试。

2.1 框架结构

基于Scrapy框架实现网络爬虫-Python

2.2 执行流程
  1. 爬虫(Spider)使用URL(要爬取页面的网址)构造一个请求(Request)对象,提交给引擎(ENGINE)。如果请求要伪装成浏览器,或者设置代理IP,可以先在爬虫中间件中设置,再发送给引擎。
  2. 引擎将请求安排给调度器,调度器根据请求的优先级确定执行顺序。
  3. 引擎从调度器获取即将要执行的请求。
  4. 引擎通过下载器中间件,将请求发送给下载器下载页面。
  5. 页面完成下载后,下载器会生成一个响应(Response)对象并将其发送给引擎。下载后的数据会保存于响应对象中。
  6. 引擎接收来自下载器的响应对象后,通过爬虫中间件,将其发送给爬虫(Spider)进行处理。
  7. 爬虫将抽取到的一条数据实体(Item)和新的请求(如下一页的链接)发送给引擎。
  8. 引擎将从爬虫获取到的Item发送给项目管道(ITEM PIPELINES),项目管道实现数据持久化等功能。同时将新的请求发送给调度器,再从第 2 步开始重复执行,直到调度器中没有更多的请求,引擎关闭该网站。
2.3 安装Scrapy

win+R,cmd,这样将安装在C盘。

>pip install scrapy

3. 开始

3.1 创建项目

首先,创建一个myScrapy的文件夹,用于存放创建的项目。由于我的Scrapy框架安装在C盘,所以文件夹和项目也放在了C盘。如果对于Scrapy的相关命令不太清楚,可以敲scrapy这个命令,会有如下图的显示。
这里是对起点中文网的数据进行爬取,所以命名为qidian_hot。
基于Scrapy框架实现网络爬虫-Python

3.2 打开项目

我这里用PyCharm打开myScrapy文件夹,目录结构如下:
基于Scrapy框架实现网络爬虫-Python
我们需要将页面爬取所设计的文件放在spiders目录下,如:qidian_hot.py。

3.3 分析页面

这里对起点中文网小说热销榜的页面的HTML进行分析,设计代码对自己所需要的数据进行抽取。
基于Scrapy框架实现网络爬虫-Python

3.4 代码如下

qidian_hot.py

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

from scrapy import Request
from scrapy.spiders import Spider
from qidian_hot.items import QidianHotItem
from scrapy.loader import ItemLoader

class HotSalesSpider(Spider):
    name = "hot"#爬虫名称
    #start_urls = ["https://www.qidian.com/rank/hotsales?style=1&page=1"]
    qidian_headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.18362"}
    current_page = 1
    
    # 如何避免爬虫被网站识别出来导致被禁呢?
    # 可以重写(override)start_requests()方法,手动生成一个功能更强大的Request对象。
    # 因为伪装浏览器、自动登录等功能都是在Request对象中设置的。
    def start_requests(self):
        url = "https://www.qidian.com/rank/hotsales?style=1&page=1"
        yield Request(url, headers=self.qidian_headers, callback=self.hot_parse)
     
    # 引擎是怎么知道要将下载好的页面发送给parse()方法而不是其他方法?能否自定义这个方法?
    # 引擎之所以能自动定位,是因为在Request对象中,指定了解析数据的回调函数,而默认情况下,Request指定的解析函数就是parse()方法。
    def hot_parse(self,response):#数据解析
        #使用xpath定位
        list_selector = response.xpath("//div[@class='book-mid-info']")
        for one_selector in list_selector:
            # #生成ItemLoader的实例
            # novel = ItemLoader(item=QidianHotItem(),selector=one_selector)
            # novel.add_xpath("name","h4/a/text()")
            # novel.add_xpath("author", "p[1]/a[1]/text()")
            # novel.add_xpath("type", "p[1]/a[2]/text()")
            # novel.add_xpath("form", "p[1]/span/text()")
            #
            # yield novel.load_item()

            #获取小说信息
            name = one_selector.xpath("h4/a/text()").extract()[0]
            #获取作者
            author = one_selector.xpath("p[1]/a[1]/text()").extract()[0]
            #获取类型
            type = one_selector.xpath("p[1]/a[2]/text()").extract()[0]
            #获取形式
            form = one_selector.xpath("p[1]/span/text()").extract()[0]

            item = QidianHotItem()
            item["name"] = name
            item["author"] = author
            item["type"] = type
            item["form"] = form

            yield item

            #获取下一页URL,并生成一个Request请求
            self.current_page += 1
            if self.current_page <= 25:
                next_url = "https://www.qidian.com/rank/hotsales?style=1&page=%d" % self.current_page
                yield Request(next_url, callback=self.hot_parse)

            # #定义字典
            # hot_dict={
            #     "name":name,
            #     "author":author,
            #     "type":type,
            #     "form":form
            # }
            # yield hot_dict

    # 如果不使用XPath而使用CSS定位,需要将Request对象中的callback设置为self.css_parse,就可将该函数指定为解析数据的回调函数。
    # 建议使用功能更强大的XPath,然后语法更简单的CSS作为XPath的辅助。 
    def css_parse(self,response):#数据解析
        #使用css定位
        list_selector = response.css("[class='book-mid-info']")
        for one_selector in list_selector:
            #获取小说信息
            name = one_selector.css("h4>a::text").extract()[0]
            #获取作者
            author = one_selector.css(".author a::text").extract()[0]
            #获取类型
            type = one_selector.css(".author a::text").extract()[0]
            #获取形式
            form = one_selector.css(".author span::text").extract()[0]
            #定义字典
            hot_dict={
                "name":name,
                "author":author,
                "type":type,
                "form":form
            }
            yield hot_dict

items.py

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

class QidianHotItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    name = scrapy.Field()
    author = scrapy.Field()
    type = scrapy.Field()
    form = scrapy.Field()
    pass

pipelines.py

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

from scrapy.exceptions import DropItem
class QidianHotPipeline(object):
    def __init__(self):
        self.author_set = set()
    def process_item(self, item, spider):
        if item["name"] in self.author_set:
            raise DropItem("查找到重复姓名的项目:%s"%item)
        return item
3.5 运行项目

进入myScrapy文件夹下的qian_hot项目文件夹下,在上方的路径处输入cmd进入命令模式,输入命令scrapy crawl hot -o hot.csv,是将运行的结果保存在hot.csv中。注意.csv文件的编码格式要与项目中的编码一致,可以在settings.py中进行查看和配置。
基于Scrapy框架实现网络爬虫-Python

3.6 运行结果

这里只是显示出运行的结果,数据将会hot.csv文件中持久化。
基于Scrapy框架实现网络爬虫-Python

上一篇:分享一个刷网页PV的python小脚本


下一篇:vue - @click 用到的修饰符