《Python網絡爬蟲技術》讀書筆記2

Scrapy爬蟲

瞭解Scrapy爬蟲框架

Scrapy是一個爬蟲框架而非功能函數庫,簡單地說,它是一個半成品,可以幫助用戶簡單快速地部署一個專業的網絡爬蟲。Scrapy爬蟲框架主要由引擎(Engine)、調度器(Scheduler)、下載器(Downloader)、Spiders、Item Pipelines、下載器中間件(Downloader Middlewares)、Spider中間件(Spider Middlewares)這7個組件構成。

引擎(Engine)

引擎負責控制數據流在系統所有組件中的流向,並在不同的條件時觸發相對應的事件。這個組件相當於爬蟲的“大腦”,是整個爬蟲的調度中心

調度器(Scheduler)

調度器從引擎接受請求並將它們加入隊列,以便之後引擎需要它們時提供給引擎。初始爬取的URL和後續在網頁中獲取的待爬取的URL都將放入調度器中,等待爬取,同時調度器會自動去除重複的URL。如果特定的URL不需要去重也可以通過設置實現,如post請求的URL

下載器(Downloader)

下載器的主要功能是獲取網頁內容,提供給引擎和Spiders

Spiders

Spiders是Scrapy用戶編寫用於分析響應,並提取Items或額外跟進的URL的一個類。每個Spider負責處理一個(一些)特定網站

Item Pipelines

Item Pipelines主要功能是處理被Spiders提取出來的Items。典型的處理有清理、驗證及持久化(例如存取到數據庫中)。當網頁被爬蟲解析所需的數據存入Items後,將被髮送到項目管道(Pipelines),並經過幾個特定的次序處理數據,最後存入本地文件或數據庫

下載器中間件(Downloader Middlewares)

下載器中間件是一組在引擎及下載器之間的特定鉤子(specific hook),主要功能是處理下載器傳遞給引擎的響應(response)。下載器中間件提供了一個簡便的機制,通過插入自定義代碼來擴展Scrapy功能。通過設置下載器中間件可以實現爬蟲自動更換user-agent、IP等功能

Spider中間件(Spider Middlewares)

Spider中間件是一組在引擎及Spiders之間的特定鉤子(specific hook),主要功能是處理Spiders的輸入(響應)和輸出(Items及請求)。Spider中間件提供了一個簡便的機制,通過插入自定義代碼來擴展Scrapy功能。各組件之間的數據流向如圖所示

scrapy基本流程

  • 引擎打開一個網站,找到處理該網站的Spiders,並向該Spiders請求第一個要爬取的URL。
  • 引擎將爬取請求轉發給調度器,調度指揮進行下一步。
  • 引擎向調度器獲取下一個要爬取的請求。
  • 調度器返回下一個要爬取的URL給引擎,引擎將URL通過下載中間件(請求方向)轉發給下載器
  • 一旦網頁下載完畢,下載器生成一個該網頁的響應,並將其通過下載中間件(返回響應方向)發送給引擎。
  • 引擎從下載器中接收到響應並通過Spider中間件(輸入方向)發送給Spiders處理。
  • Spiders處理響應並返回爬取到的Items及(跟進)新的請求給引擎。
  • 引擎將(Spiders返回的)爬取到的Items給Item Pipeline,將(Spiders返回的)請求給調度器。
  • 從第2步重複直到調度器中沒有更多的URL請求,引擎關閉該網站。

熟悉Scrapy常用命令

Scrapy通過命令行進行控制,Scrapy提供了多種命令,用於多種目的,並且每個命令都接收一組不同的參數和選項

在這裏插入圖片描述

除了全局命令外,Scrapy還提供了專用於項目的項目命令

在這裏插入圖片描述

創建Scrapy爬蟲項目

命令行下面使用

scrapy startproject <project_name> [project_dir]

這裏是引用

在這裏插入圖片描述

下面的步驟生成一TipDMSpider的爬蟲,爬取目標網頁

http://www.tipdm.org/bdrace/notices/

將內容保存到excel中

新建項目

命令行執行

scrapy startproject TipDMSpider

可以看到創建的文件夾內容如下

在這裏插入圖片描述

爬蟲的主要目標就是從網頁這一非結構化的數據源中提取結構化的數據。TipDMSpider項目最終的目標是解析出文章的標題(title)、時間(time)、正文(text)、瀏覽數(view_count)等數據。Scrapy提供Item對象來完成解析數據轉換爲結構化數據的功能。TipDMSpider項目提取的信息最終將存儲至csv文件與數據庫。使用pandas庫將Items中的數據轉換爲DataFrame會更方便處理

修改items腳本

爬蟲的主要目標就是從網頁這一非結構化的數據源中提取結構化的數據。TipDMSpider項目最終的目標是解析出文章的標題(title)、時間(time)、正文(text)、瀏覽數(view_count)等數據。Scrapy提供Item對象來完成解析數據轉換爲結構化數據的功能

分析目標頁需要獲取的內容如下

在這裏插入圖片描述

修改items的代碼如下

class TipdmspiderItem(scrapy.Item):
    title = scrapy.Field()
    time = scrapy.Field()
    source = scrapy.Field()
    text = scrapy.Field()
    pass

修改setting

在setting下開啓ITEM_PIPELINES後才能使得到的items進入pipeline處理

ITEM_PIPELINES = {
   'TipDMSpider.pipelines.TipdmspiderPipeline': 300,
}
HTTPCACHE_ENABLED = True
HTTPCACHE_DIR = 'C:\\Users\\zeng\\PycharmProjects\\spider\\TipDMSpider\\TipDMSpider'

在這裏插入圖片描述

項目的默認settings腳本中共有25個設置

在這裏插入圖片描述

在這裏插入圖片描述

在這裏插入圖片描述

修改piplines

items的內容將會流向piplines,piplines的作用就是講獲取到的數據進行清理、驗證後持久化

import pandas as pd

class TipdmspiderPipeline(object):
    def process_item(self, item, spider):
        data = pd.DataFrame(dict(item))
        data.to_csv('output_data.csv', mode='a+', index=False, sep='|', header=False)
        return item

編寫spider腳本

使用命令行在當前路徑下新建一個爬蟲入口文件

scrapy genspider tipdm www.tipdm.org

在spiders文件夾中可以看到生成出來的tipdm.py

在這裏插入圖片描述

定製中間件

中間件能夠完成很多任務,比如實現IP代理,改變下載頻率,限制最大爬取深度、篩選未成功響應、去重等等

定製下載器中間件

在項目中的middlewares.py文件中,是中間件的代碼,在最底下添加兩個class,是我們自定定義的中間件,第一個爲隨機代理,另外一個爲隨機ua

from scrapy.utils.python import to_bytes
from six.moves.urllib.parse import unquote
from six.moves.urllib.parse import urlunparse
import base64
import random
try:
    from urllib2 import _parse_proxy
except ImportError:
    from urllib.request import _parse_proxy

class ProxyMiddleware(object):
    def _basic_auth_header(self, username, password):
        user_pass = to_bytes(
            '%s:%s' % (unquote(username), unquote(password)),
            encoding='latin-1')
        return base64.b64encode(user_pass).strip()

    def process_request(self, request, spider):
        PROXIES = [
            "http://zeng:[email protected]:808/",
        ]
        url = random.choice(PROXIES)
        orig_type = ""
        proxy_type, user, password, hostport = _parse_proxy(url)
        proxy_url = urlunparse((proxy_type or orig_type, hostport, '', '', '', ''))
        if user:
            creds = self._basic_auth_header(user, password)
        else:
            creds = None
        request.meta['proxy'] = proxy_url
        print("當前使用代理:", url)
        if creds:
            request.headers['Proxy-Authorization'] = b'Basic ' + creds

from scrapy.downloadermiddlewares.useragent import UserAgentMiddleware
user_agent_list = [
    "Mozilla/5.0 (X11; U; Linux x86_64; zh-CN; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10",
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36",
    ]
class UASpiderMiddleware(UserAgentMiddleware):
    def __init__(self, user_agent):
        self.user_agent = user_agent_list

    # 設置User-Agent
    def process_request(self, request, spider):
        agent = random.choice(self.user_agent)
        request.headers['User-Agent'] = agent
        print('當前User-Agent:', agent)

激活中間件

在項目的setting.py文件中,還有很多其他信息,現在只需要關注以下幾個,首先激活剛加入的兩個自定義中間件,然後打開默認的一個refer中間件,找到DOWNLOADER_MIDDLEWARES,打開註釋,寫入如下內容

DOWNLOADER_MIDDLEWARES = {
   'TipDMSpider.middlewares.TipdmspiderDownloaderMiddleware': 543,
   'TipDMSpider.middlewares.ProxyMiddleware': 544,
   'scrapy.spidermiddlewares.referer.DefaultReferrerPolicy': 545,
   'TipDMSpider.middlewares.UASpiderMiddleware': 546,
   'scrapy.downloadermiddlewares.robotstxt.RobotsTxtMiddleware': 0 # 關閉內置下載器中間件robots
}

聲明不遵守爬蟲協議,設置下載延遲爲5秒

# Obey robots.txt rules
ROBOTSTXT_OBEY = True
DOWNLOAD_DELAY = 5

打開緩存的註釋,並修改成如下

HTTPCACHE_ENABLED = True
HTTPCACHE_DIR = 'C:\\Users\\zeng\\PycharmProjects\\spider\\TipDMSpider\\TipDMSpider'

測試整個項目

在命令行執行

scrapy crawl tipdm

啓動爬蟲,可以看到中間的輸出,和最後的csv文件

在這裏插入圖片描述

在這裏插入圖片描述

題外話,使用自定義url去重

在項目中編寫一個去重的py文件,duplicate_filter.py,內容如下

from scrapy.dupefilter import BaseDupeFilter
from scrapy.utils.request import request_fingerprint

"""
1. 根據配置文件找到 DUPEFILTER_CLASS = 'xianglong.dupe.MyDupeFilter'
2. 判斷是否存在from_settings
    如果有:
        obj = MyDupeFilter.from_settings()
    否則:
        obj = MyDupeFilter()
"""
class MyDupeFilter(BaseDupeFilter):
    def __init__(self):
        self.record = set()

    @classmethod
    def from_settings(cls, settings):
        return cls()

    #:return: True表示已經訪問過;False表示未訪問過
    def request_seen(self, request):
        ident = request_fingerprint(request)
        if ident in self.record:
            print('已經訪問過了', request.url)
            return True
        print("=================新發現的url=================")
        self.record.add(ident)

    def open(self):  # can return deferred
        pass

    def close(self, reason):  # can return a deferred
        pass

在setting中使用整個filter

DUPEFILTER_CLASS = 'TipDMSpider.duplicate_filter.MyDupeFilter'

在我們的tipdm.py中,也就是spider文件的入口,去掉

dont_filter=True

這樣去重class纔會生效

# 提取每一個分頁中的每一條新聞
def parse_url(self, response):
    # 取出ul標籤
    urls = response.xpath("//a[@class='titA']/@href").extract()
    # 循環取出裏面的li鏈接
    for url in urls:
        article_url = "http://www.tipdm.org" + url
        yield Request(article_url, callback=self.parse_item)
    for url in urls:
        article_url = "http://www.tipdm.org" + url
        # yield Request(article_url, callback=self.parse_item, dont_filter=True)
        yield Request(article_url, callback=self.parse_item)

在這裏插入圖片描述

參考鏈接

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章