爬蟲框架 scrapy 詳解


 1、什么是scrapy

  Scrapy是一個為了爬取網站數據,提取結構性數據而編寫的應用框架,我們只需要實現少量的代碼,就能夠快速的抓取。Scrapy 使用了Twisted['twɪstɪd]異步網絡框架

  文檔地址:https://scrapy-chs.readthedocs.io/zh_CN/1.0/topics/commands.html

2、scrapy環境配置  

pip install Scrapy

3、scrapy的流程

 

 

 

其流程可以描述如下:

  1. 調度器把requests-->引擎-->下載中間件--->下載器
  2. 下載器發送請求,獲取響應---->下載中間件---->引擎--->爬蟲中間件--->爬蟲
  3. 爬蟲提取url地址,組裝成request對象---->爬蟲中間件--->引擎--->調度器
  4. 爬蟲提取數據--->引擎--->管道
  5. 管道進行數據的處理和保存

注意:

  • 圖中綠色線條的表示數據的傳遞
  • 注意圖中中間件的位置,決定了其作用
  • 注意其中引擎的位置,所有的模塊之前相互獨立,只和引擎進行交互

scrapy中每個模塊的具體作用

4、創建scrapy項目

# 命令:
scrapy startproject +<項目名字>

#示例:
scrapy startproject myspider

  生成的目錄和文件結果如下:

5、 創建爬蟲

  首先進入創建的項目目錄里面的spiders目錄,然后執行下面的命令創建爬蟲

# 命令:
scrapy genspider +<爬蟲名字> + <允許爬取的域名>

# 示例:
scrapy genspider itcast itcast.cn

6、完善spider, 將數據傳遞到pipeline

  完善spider即通過方法進行數據的提取等操作 

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


class ItcastSpider(scrapy.Spider):
    name = 'itcast'  # 爬蟲名  [爬蟲啟動時,使用scrapy crawl itcast]
    allowed_domains = ['itcast.cn']  # 允許爬取的范圍,防止爬蟲爬到其他網站
    start_urls = ['http://www.itcast.cn/channel/teacher.shtml']  # 爬蟲最開始抓取的url地址

    def parse(self, response):  # 數據提取方法,處理start_url地址中的響應,接受下載中間件傳過來的response響應

        # 先分組,再進行數據的提取
        li_list = response.xpath('//div[@class="tea_con"]/div/ul/li')
        for li in li_list:
            item = {}
            item['name'] = li.xpath('.//h3/text()').extract_first()
            item['title'] = li.xpath('.//h4/text()').extract_first()
            item['desc'] = li.xpath('.//p/text()').extract_first()
            print(item)
            yield item   # 將數據傳給pipeline 

注意:

  1. response.xpath方法的返回結果是一個類似list的類型,其中包含的是selector對象,操作和列表一樣,但是有一些額外的方法
  2. extract() 返回一個包含有字符串的列表
  3. extract_first() 返回列表中的第一個字符串,列表為空沒有返回None
  4. spider中的parse方法必須有
  5. 需要抓取的url地址必須屬於allowed_domains,但是start_urls中的url地址沒有這個限制
  6. 啟動爬蟲的時候注意啟動的位置,是在項目路徑下啟動

為什么要使用yield?

  • 讓整個函數變成一個生成器,有什么好處呢?
  • 遍歷這個函數的返回值的時候,挨個把數據讀到內存,不會造成內存的瞬間占用過高
  • python3中的range和python2中的xrange同理

注意:

  • yield能夠傳遞的對象只能是:BaseItemRequestdictNone

7、完善pipeline 

pipeline在settings中能夠開啟多個,為什么需要開啟多個?

  • 不同的pipeline可以處理不同爬蟲的數據
  • 不同的pipeline能夠進行不同的數據處理的操作,比如一個進行數據清洗,一個進行數據的保存

pipeline使用注意點

  • 使用之前需要在settings中開啟
  • pipeline在setting中鍵表示位置(即pipeline在項目中的位置可以自定義),值表示距離引擎的遠近,越近數據會越先經過
  • 有多個pipeline的時候,process_item的方法必須return item,否則后一個pipeline取到的數據為None值
  • pipeline中process_item的方法必須有,否則item沒有辦法接受和處理
  • process_item方法接受item和spider,其中spider表示當前傳遞item過來的spider

8、 scrapy啟動爬蟲項目

# 進去爬蟲項目目錄,執行以下命令
scrapy crawl "爬蟲名"

8、 scrapy實現翻頁請求

對於要提取如下圖中所有頁面上的數據該怎么辦?

回顧requests模塊是如何實現翻頁請求的:

  • 找到下一頁的URL地址
  • 調用requests.get(url)

思路:

  1. 找到下一頁的url地址
  2. 構造url地址的請求,傳遞給引擎

8.1 實現翻頁請求

  1. 使用方法

    在獲取到url地址之后,可以通過scrapy.Request(url,callback)得到一個request對象,通過yield關鍵字就可以把這個request對象交給引擎

  2. 具體使用 
# scrapy.Request的參數介紹

scrapy.Request(url[,callback,method="GET",headers,body,cookies,meta,dont_filter=False])

# 括號中的參數為可選參數
# callback:表示當前的url的響應交給哪個函數去處理
# meta:實現數據在不同的解析函數中傳遞,meta默認帶有部分數據,比如# 下載延遲,請求深度等
# dont_filter:默認會過濾請求的url地址,即請求過的url地址不會繼續被請求,對需要重復請求的url地址可以把它設置為Ture,比如貼吧的翻頁請求,頁面的數據總是在變化;start_urls中的地址會被反復請求,否則程序不會啟動
# -*- coding: utf-8 -*-
import re
from copy import deepcopy
import scrapy


class SnSpider(scrapy.Spider):
    name = 'sn'
    allowed_domains = ['suning.com']
    start_urls = ['https://book.suning.com/']

    def parse(self, response):
           # 解析數據
           pass
           # 獲取下一頁url地址
           next_part_url = ""
           yield scrapy.Request(next_part_url, callback=self.parse_book_list, meta={"item": deepcopy(item)})
           # scrapy.Request()能構建一個request對象,同時指定提取數據的callback函數 

    def parse_next_list(self, response):
        # 處理下一頁的數據提取            

  3. 添加User-Agent

  同時可以再在setting中設置User-Agent:

USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36'

9、 定義Item

  1. 定義Item的原因

    定義item即提前規划好哪些字段需要抓取,scrapy.Field()僅僅是提前占坑,通過item.py能夠讓別人清楚自己的爬蟲是在抓取什么,同時定義好哪些字段是需要抓取的,沒有定義的字段不能使用,防止手2

  2. 定義Item

     

  3. 使用Item

    Item使用之前需要先導入並且實例化,之后的使用方法和使用字典相同   

 from yangguang.items import YangguangItem
 item = YangguangItem() #實例化  

10、 輸出日志LOG的設置

  為了讓我們自己希望輸出到終端的內容能容易看一些,我們可以在setting中設置log級別

  在setting中添加一行(全部大寫):LOG_LEVEL = "WARNING”

  默認終端顯示的是debug級別的log信息

  scrapy的debug信息

  每次程序啟動后,默認情況下,終端都會出現很多的debug信息,那么下面我們來簡單認識下這些信息

  

11、認識scrapy中的setting文件

  • 為什么項目中需要配置文件

    • 在配置文件中存放一些公共變量,在后續的項目中便便修改,注意其中的變量名一般全部大寫
  • 配置文件中的變量使用方法

    • 導入即可使用
  • settings.py中的重點字段和內涵

# USER_AGENT 設置ua
# ROBOTSTXT_OBEY 是否遵守robots協議,默認是遵守
# CONCURRENT_REQUESTS 設置並發請求的數量,默認是16個
# DOWNLOAD_DELAY 下載延遲,默認無延遲
# COOKIES_ENABLED 是否開啟cookie,即每次請求帶上前一次的cookie,默認是開啟的
# DEFAULT_REQUEST_HEADERS 設置默認請求頭
# SPIDER_MIDDLEWARES 爬蟲中間件,設置過程和管道相同
# DOWNLOADER_MIDDLEWARES 下載中間件

12、管道中的open_spiderclose_spider 的方法

  在管道中,除了必須定義process_item之外,還可以定義兩個方法:

  • open_spider(spider) :能夠在爬蟲開啟的時候執行一次
  • close_spider(spider) :能夠在爬蟲關閉的時候執行一次

  所以,上述方法經常用於爬蟲和數據庫的交互,在爬蟲開啟的時候建立和數據庫的連接,在爬蟲關閉的時候斷開和數據庫的連接

  下面的代碼分別以操作文件和mongodb為例展示方法的使用:

  

 

13、crawlspider類的使用

  1. crawlspider是什么

  回顧之前的代碼中,我們有很大一部分時間在尋找下一頁的url地址或者是內容的url地址上面,這個過程能更簡單一些么?

  思路:

  • 從response中提取所有的滿足規則的url地址
  • 自動的構造自己requests請求,發送給引擎

  對應的crawlspider就可以實現上述需求,匹配滿足條件的url地址,才發送給引擎,同時能夠指定callback函數

  2. 認識crawlspider爬蟲

  2.1 創建crawlspdier爬蟲的命令

scrapy genspider –t crawl itcast itcast.cn

  2.2 觀察爬蟲內的默認內容

  spider中默認生成的內容如下,其中重點在rules中

  • rules是一個元組或者是列表,包含的是Rule對象
  • Rule表示規則,其中包含LinkExtractor,callbackfollow
  • LinkExtractor:連接提取器,可以通過正則或者是xpath來進行url地址的匹配
  • callback :表示經過連接提取器提取出來的url地址響應的回調函數,可以沒有,沒有表示響應不會進行回調函數的處理
  • follow:表示進過連接提取器提取的url地址對應的響應是否還會繼續被rules中的規則進行提取,True表示會,Flase表示不會
class Itcast1Spider(CrawlSpider):
    name = 'itcast1'
    allowed_domains = ['itcast.cn']
    start_urls = ['http://itcast.cn/']

    rules = (
        Rule(LinkExtractor(allow=r'Items/'), callback='parse_item', follow=True),
    )

    def parse_item(self, response):
        i = {}
        #使用xpath進行數據的提取或者url地址的提取
        return i

  2.3 crawlspider的使用

  指定兩個正則規則,匹配url

    rules = ( Rule(LinkExtractor(allow=r'position_detail.php\?id=\d+&keywords=&tid=0&lid=0'), callback='parse_item'), Rule(LinkExtractor(allow=r'position.php\?&start=\d+#a'), follow=True), )
# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule


class TcSpider(CrawlSpider):
    name = 'tc'
    allowed_domains = ['tencent.com']
    start_urls = ['https://hr.tencent.com/position.php']

    rules = (
        Rule(LinkExtractor(allow=r'position_detail.php\?id=\d+&keywords=&tid=0&lid=0'), callback='parse_item'),
        Rule(LinkExtractor(allow=r'position.php\?&start=\d+#a'), follow=True),
    )

    def parse_item(self, response):
        item = {}
        # i['domain_id'] = response.xpath('//input[@id="sid"]/@value').extract()
        # i['name'] = response.xpath('//div[@id="name"]').extract()
        # i['description'] = response.xpath('//div[@id="description"]').extract()
        item["title"] = response.xpath("//td[@id='sharetitle']/text()").extract_first()  # 提取標題
        item['duty'] = response.xpath("//div[text()='工作職責:']/following-sibling::ul[1]/li/text()").extract()
        item['require'] = response.xpath("//div[text()='工作要求:']/following-sibling::ul[1]/li/text()").extract()
        print(item)

  2.4 crawlspider使用的注意點

  2.5 crawlspider的補充知識點(了解)

14、下載中間件和模擬登陸

  14.1  scrapy中下載中間件的使用

  1. 使用方法:

    編寫一個Downloader Middlewares和我們編寫一個pipeline一樣,定義一個類,然后在setting中開啟

  2. Downloader Middlewares默認的方法:

    • process_request(self, request, spider):

當每個request通過下載中間件時,該方法被調用。
返回None值:繼續請求
返回Response對象:不在請求,把response返回給引擎
返回Request對象:把request對象交給調度器進行后續的請求

 

    • process_response(self, request, response, spider):

 

 - 當下載器完成http請求,傳遞響應給引擎的時候調用
 - 返回Resposne:交給process_response來處理
 - 返回Request對象:交給調取器繼續請求

  3. 定義實現隨機User-Agent的下載中間件

 class UserAgentMiddleware(object):
     def process_request(self,request,spider):
         agent = random.choice(agents)
         request.headers['User-Agent'] = agent

  4. 定義實現隨機使用代理的下載中間件

 class ProxyMiddleware(object):
     def process_request(self,request,spider):
         proxy = random.choice(proxies)
         request.meta['proxy'] = proxy

  14.2. 使用scrapy進行模擬登陸

  2.1 回顧之前的模擬登陸的方法

  1. requests是如何模擬登陸的?

    1. 直接攜帶cookies請求頁面
    2. 找接口發送post請求存儲cookie
  2. selenium是如何模擬登陸的?

    1. 找到對應的input標簽,輸入文字點擊登錄

  scrapy來說,有兩個方法模擬登陸:

  1、直接攜帶cookie
  2、找到發送post請求的url地址,帶上信息,發送請求

  2.2 scrapy攜帶cookie進行模擬登陸

  1. 攜帶cookie進行模擬登陸應用場景:

    1. cookie過期時間很長,常見於一些不規范的網站
    2. 能在cookie過期之前把搜有的數據拿到
    3. 配合其他程序使用,比如其使用selenium把登陸之后的cookie獲取到保存到本地,scrapy發送請求之前先讀取本地cookie
  2. scrapy的start_requests方法的學習

    scrapy中start_url是通過start_requests來進行處理的,其實現代碼如下 

 def start_requests(self):
     cls = self.__class__
     if method_is_overridden(cls, Spider, 'make_requests_from_url'):
         warnings.warn(
             "Spider.make_requests_from_url method is deprecated; it "
             "won't be called in future Scrapy releases. Please "
             "override Spider.start_requests method instead (see %s.%s)." % (
                 cls.__module__, cls.__name__
             ),
         )
         for url in self.start_urls:
             yield self.make_requests_from_url(url)
     else:
         for url in self.start_urls:
             yield Request(url, dont_filter=True)

  所以對應的,如果start_url地址中的url是需要登錄后才能訪問的url地址,則需要重寫start_request方法並在其中手動添加上cookie

  3. 實現攜帶cookie登錄人人網,例如:

  注意:scrapy中cookie不能夠放在headers中,在構造請求的時候有專門的cookies參數,能夠接受字典形式的coookie

import scrapy
import re

class RenrenSpider(scrapy.Spider):
    name = 'renren'
    allowed_domains = ['renren.com']
    start_urls = ['http://www.renren.com/941954027/profile']

    def start_requests(self):
        cookie_str = "cookie_str"
        cookie_dict = {i.split("=")[0]:i.split("=")[1] for i in cookie_str.split("; ")}
        yield scrapy.Request(
            self.start_urls[0],
            callback=self.parse,
            cookies=cookie_dict,
            # headers={"Cookie":cookie_str}
        )

    def parse(self, response):
        ret = re.findall("新用戶287",response.text)
        print(ret)
        yield scrapy.Request(
            "http://www.renren.com/941954027/profile?v=info_timeline",
            callback=self.parse_detail
        )

    def parse_detail(self,response):
        ret = re.findall("新用戶287",response.text)
        print(ret)

     4. 在settings中開啟cookie_debug

  在settings.py中通過設置COOKIES_DEBUG=TRUE 能夠在終端看到cookie的傳遞傳遞過程

  

 

  2.3 scrapy發送post請求

  scrapy中發送post請求的方法 通過scrapy.FormRequest能夠發送post請求,同時需要添加fromdata參數作為請求體,以及callback 

  使用scrapy模擬登陸github

  思路分析

  1. 找到post的url地址

    點擊登錄按鈕進行抓包,然后定位url地址為https://github.com/session

  2. 找到請求體的規律

    分析post請求的請求體,其中包含的參數均在前一次的響應中

  3. 驗證是否登錄成功

    通過請求個人主頁,觀察是否包含用戶名

#spider/github.py
# -*- coding: utf-8 -*-
import scrapy
import re

class GithubSpider(scrapy.Spider):
 name = 'github'
 allowed_domains = ['github.com']
 start_urls = ['https://github.com/login']

 def parse(self, response):
     authenticity_token = response.xpath("//input[@name='authenticity_token']/@value").extract_first()
     utf8 = response.xpath("//input[@name='utf8']/@value").extract_first()
     commit = response.xpath("//input[@name='commit']/@value").extract_first()

     yield scrapy.FormRequest(
         "https://github.com/session",
         formdata={
             "authenticity_token":authenticity_token,
             "utf8":utf8,
             "commit":commit,
             "login":"noobpythoner",
             "password":"***"
         },
         callback=self.parse_login
     )

 def parse_login(self,response):
     ret = re.findall("noobpythoner",response.text,re.I)
     print(ret)

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM