天天看點

【Python】Scrapy爬蟲介紹&&requests爬蟲移植到Scrapy爬蟲ScrapyScrapy爬蟲執行個體編寫/re爬蟲移植

【Python】Scrapy爬蟲介紹&&re爬蟲移植到Scrapy爬蟲

  • Scrapy
    • Scrapy爬蟲架構
      • requests和Scrapy對比
      • Scrapy常用指令
    • Scrapy 爬蟲基本使用
      • 第一個Scrapy執行個體
      • Scrapy爬蟲的基本使用
  • Scrapy爬蟲執行個體編寫/re爬蟲移植
    • 豆瓣Top100爬蟲
    • 豆瓣Top100爬蟲源re代碼
    • 環境配置參考
    • 執行個體程式參考
    • 程式編寫
      • 步驟1:建立工程
      • 步驟2:建立Spider.模闆
      • 步驟3:編寫 Spider
      • 步驟4:配置并發連接配接選項,優化爬取速度:編寫 Pipelines
    • 爬取豆瓣top250的scrapy爬蟲
      • 參考
      • douban.py
      • pipelines處理資料,儲存檔案
      • settings.py設定檔案
      • 運作爬蟲
      • 報錯處理
      • 修正後的`douban.py`版本

Scrapy

Scrapy爬蟲架構

Scrapy爬蟲架構結構

爬蟲架構

爬蟲架構是實作爬蟲功能的一個軟體結構和功能元件集合。

爬蟲架構是一個半成品,能夠幫助使用者實作專業網絡爬蟲。

五個主要子產品,兩個中間件。

【Python】Scrapy爬蟲介紹&&requests爬蟲移植到Scrapy爬蟲ScrapyScrapy爬蟲執行個體編寫/re爬蟲移植

Engine(不需要使用者修改)

  • 控制所有子產品之間的資料流
  • 根據條件觸發事件

Downloader

  • 根據請求下載下傳網頁
  • 不需要使用者修改

Scheduler

  • 不需要使用者修改
  • 對所有爬取請求進行排程管理

Spider(最核心)

  • 解析Downloader傳回的響應(Response)
  • 産生爬取項(Scraped item)
  • 産生額外的爬取請求(Request)

Item Pipelines

  • 以流水線處理Spider的爬取項
  • 由一組操作順序組成,類似一個流水線,每個操作是一個Item Pipeline類型。
  • 可能操作包括:清理,檢驗和查重爬取項中的HTML資料,将資料儲存到資料庫。

中間件:Downloader Middleware

  • 目的:實施 Engine、 Scheduler和 Downloader之間進行使用者可配置的控制

    功能:修改、丢棄、新增請求或響應

Spider Middleware

  • 目的:對請求和爬取項的再處理

    功能:修改、丢棄、新增請求或爬取項

    使用者可以編寫配置代碼

requests和Scrapy對比

【Python】Scrapy爬蟲介紹&&requests爬蟲移植到Scrapy爬蟲ScrapyScrapy爬蟲執行個體編寫/re爬蟲移植
【Python】Scrapy爬蟲介紹&&requests爬蟲移植到Scrapy爬蟲ScrapyScrapy爬蟲執行個體編寫/re爬蟲移植

Scrapy常用指令

【Python】Scrapy爬蟲介紹&&requests爬蟲移植到Scrapy爬蟲ScrapyScrapy爬蟲執行個體編寫/re爬蟲移植
【Python】Scrapy爬蟲介紹&&requests爬蟲移植到Scrapy爬蟲ScrapyScrapy爬蟲執行個體編寫/re爬蟲移植

Scrapy 爬蟲基本使用

第一個Scrapy執行個體

【Python】Scrapy爬蟲介紹&&requests爬蟲移植到Scrapy爬蟲ScrapyScrapy爬蟲執行個體編寫/re爬蟲移植
【Python】Scrapy爬蟲介紹&&requests爬蟲移植到Scrapy爬蟲ScrapyScrapy爬蟲執行個體編寫/re爬蟲移植
【Python】Scrapy爬蟲介紹&&requests爬蟲移植到Scrapy爬蟲ScrapyScrapy爬蟲執行個體編寫/re爬蟲移植

DemoSpider這個類叫什麼名字無所謂,隻要他是繼承于scrapy.Spider的子類即可。

parse()用于處理響應,解析内容形成字典,發現新的URL爬取請求。

産生步驟

  • 步驟1:建立一個 Scrapy爬蟲工程
  • 步驟2:在工程中産生一個 Scrapy爬蟲
  • 步驟3:配置産生的 spider爬蟲
  • 步驟4:運作爬蟲,擷取網頁。
    【Python】Scrapy爬蟲介紹&&requests爬蟲移植到Scrapy爬蟲ScrapyScrapy爬蟲執行個體編寫/re爬蟲移植
cd pycodes
scrapy startproject python123demo
cd python123demo
scrapy genspider demo python123.io#生成一個名稱為demo的spider,生成了demo.py
           

yield關鍵字。生成器。生成器是一個不斷産生值的函數。

包含 yield語句的函數是一個生成器

生成器每次産生一個值( yield語句),函數被當機,被喚醒後再産生一個值。

【Python】Scrapy爬蟲介紹&&requests爬蟲移植到Scrapy爬蟲ScrapyScrapy爬蟲執行個體編寫/re爬蟲移植

生成器,産生小于n的數的平方值。并将所有的傳回值傳回給上層調用函數。

【Python】Scrapy爬蟲介紹&&requests爬蟲移植到Scrapy爬蟲ScrapyScrapy爬蟲執行個體編寫/re爬蟲移植

普通寫法:存儲所有的數。

生成器寫法:每次隻要一個值。每次的存儲空間是一個的存儲空間。

當n很大時,生成器就有很大的優勢。

【Python】Scrapy爬蟲介紹&&requests爬蟲移植到Scrapy爬蟲ScrapyScrapy爬蟲執行個體編寫/re爬蟲移植

Scrapy爬蟲的基本使用

Scrapy爬蟲的使用步驟

  • 步驟1:刨建一個工程和 Spider模闆
  • 步驟2:編寫 Spider
  • 步驟3:編寫 Item Pipeline
  • 步驟4:優化配置政策

Scrap爬蟲的資料類型

  • Request類
  • Response類
  • Item類

Request類

class scrapy.http.Request()

Request對象表示ー個HTTP請求。

由 Spider生成,由 Downloader.執行。

【Python】Scrapy爬蟲介紹&&requests爬蟲移植到Scrapy爬蟲ScrapyScrapy爬蟲執行個體編寫/re爬蟲移植

Response類

class scrapy.http.Response()

Response對象表示一個HTTP響應。

由 Downloader生成,由 Spider處理。

【Python】Scrapy爬蟲介紹&&requests爬蟲移植到Scrapy爬蟲ScrapyScrapy爬蟲執行個體編寫/re爬蟲移植

Item類

class scrap.item.Item()

Item對象表示一個從HTML頁面中提取的資訊内容。

由 Spider生成,由 Item Pipelines處理。

爬取資訊,封裝成字典,存儲。

Scrapy爬蟲支援多種HTML資訊提取方法

  • Beautiful Soup
  • lxml
  • re
  • Xpath Selector
  • CSS Selector
    【Python】Scrapy爬蟲介紹&&requests爬蟲移植到Scrapy爬蟲ScrapyScrapy爬蟲執行個體編寫/re爬蟲移植

Scrapy爬蟲執行個體編寫/re爬蟲移植

豆瓣Top100爬蟲

技術路線: scrapy

目标:擷取上交所和深交所所有股票的名稱和交易資訊

輸出:儲存到檔案中

豆瓣Top100爬蟲源re代碼

import re
import requests
from bs4 import BeautifulSoup
import xlwt
import time
def get_one_page(url):
	hd={
	'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36'
}
	try:
		r = requests.get(url, timeout=30,headers=hd)
		r.raise_for_status()
		r.encoding = r.apparent_encoding
		return r.text
	except:
		return "geturl wrong!"


def parser_one_page(s):
	s=re.sub('<br>',' ',s)
	s=re.sub('&nbsp;','',s)
	soup=BeautifulSoup(s,"html.parser")
	list=soup.find_all('div','item')
	for item in list: 
		item_name = item.find(class_='title').string
		item_img = item.find('a').find('img').get('src')
		item_index = item.find(class_='').string
		item_score = item.find(class_='rating_num').string
		item_author = item.find('p').text
		if (item.find(class_='inq') != None):
			item_intr = item.find(class_='inq').string
		print('爬取電影:' + item_index + ' | ' + item_name +' | ' + item_img +' | ' + item_score +' | ' + item_author +' | ' + item_intr )
		#print('爬取電影:' + item_index + ' | ' + item_name + ' | ' + item_score + ' | ' + item_intr+' | '+item_img)
		global n
		sheet.write(n, 0, item_name)
		sheet.write(n, 1, item_img)
		sheet.write(n, 2, item_index)
		sheet.write(n, 3, item_score)
		sheet.write(n, 4, item_author)
		sheet.write(n, 5, item_intr)
		n = n + 1


if __name__ == '__main__':
	book = xlwt.Workbook(encoding='utf-8', style_compression=0)
	sheet = book.add_sheet('豆瓣電影Top250', cell_overwrite_ok=True)
	sheet.write(0, 0, '名稱')
	sheet.write(0, 1, '圖檔')
	sheet.write(0, 2, '排名')
	sheet.write(0, 3, '評分')
	sheet.write(0, 4, '作者')
	sheet.write(0, 5, '簡介')
	n= 1
	urls=['https://movie.douban.com/top250?start={}&filter='.format(str(i)) for i in range(0,250,25)]
	for url in urls:
		html=get_one_page(url)
		parser_one_page(html)
		time.sleep(2)
	book.save(u'豆瓣最受歡迎的250部電影.csv')
           

環境配置參考

pycharm中建立scrapy項目

執行個體程式參考

import scrapy


class NewsSpider(scrapy.Spider):
    name = 'news'
    allowed_domains = ['hitwh.edu.cn']
    start_urls = ['http://today.hitwh.edu.cn/1024/list.htm']

    def decode(self,s):
        return bytes(s,encoding='utf-8')
    def parse(self, response):
        num = response.css(".all_pages ::text").extract()[0];
        for i in range(int(num)):
            try:
                url = "http://today.hitwh.edu.cn/1024/list"+str(i+1)+".htm"
                # print(url)
                yield scrapy.Request(url,callback=self.parse_pages)
            except:
                print("WRONG ON PAGE",str(i+1))
                continue
    def parse_pages(self,response):
        # fname = "result.txt"
        # # print("unbegin")
        #with open(fname,"wb") as f:
        # total={}
        for item in response.css("#righ_list li"):
            url= "http://today.hitwh.edu.cn"+item.css("a ::attr(href)").extract()[0]
            yield scrapy.Request(url,callback=self.parse_detail)
            
            # total.update({"題目:":item.css("a ::text").extract()[0],
            # "時間":item.css("font ::text").extract()[2]})
            
            #print(item.css("a ::text").extract()[0]);
            # f.write(self.decode(item.css("a ::text").extract()[0]))
            # f.write(self.decode(item.css("font ::text").extract()[2]))
            # f.write(self.decode("\n"))  
                
                # print(item.css("font ::text").extract());    
        
        #     f.write(str(response.css("#righ_list a::text")))
        # self.log('Saved file %s.' % fname)
    def parse_detail(self,response):
    # fname = "result.txt"
    # # print("unbegin")
    #with open(fname,"wb") as f:
        total={}
        time = response.css(".newsNav ::text").extract()[2]
        time = time[6:-2]
        author = response.css(".newsNav ::text").extract()[0]
        author = author[5:-4]
        if (author==''):
            author="無"
        total.update({"題目":response.css(".newsTitle ::text").extract()[0],"時間":time,"作者":author})
        yield total




# pipelines

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


# useful for handling different item types with a single interface
from itemadapter import ItemAdapter
import json

class HitwhPipeline:
    def process_item(self, item, spider):
        return item
class pagePipeline:
    def open_spider(self,spider):
        self.f = open("information.json","w",encoding='utf-8')
    def close_spider(self,spider):
        self.f.close()
    def process_item(self, item, spider):
        try:
            line=str(json.dumps(dict(item),ensure_ascii=False))+"\n"
            self.f.write(line)
        except Exception as e:
            print (str(e))
            print("ERROR")
            pass
        return item
           

程式編寫

步驟

步驟1:建立工程和 Spider模闆

步驟2:編寫 Spider

步驟3:編寫 ITEM Pipelines

步驟1:建立工程

打開pycharm,自己在自己喜歡的路徑下建立一個項目

【Python】Scrapy爬蟲介紹&amp;&amp;requests爬蟲移植到Scrapy爬蟲ScrapyScrapy爬蟲執行個體編寫/re爬蟲移植
【Python】Scrapy爬蟲介紹&amp;&amp;requests爬蟲移植到Scrapy爬蟲ScrapyScrapy爬蟲執行個體編寫/re爬蟲移植

步驟2:建立Spider.模闆

scrapy startproject Web_scrapy
cd Web_scrapy
scrapy genspider douban movie.douban.com/top250
           
【Python】Scrapy爬蟲介紹&amp;&amp;requests爬蟲移植到Scrapy爬蟲ScrapyScrapy爬蟲執行個體編寫/re爬蟲移植
【Python】Scrapy爬蟲介紹&amp;&amp;requests爬蟲移植到Scrapy爬蟲ScrapyScrapy爬蟲執行個體編寫/re爬蟲移植
【Python】Scrapy爬蟲介紹&amp;&amp;requests爬蟲移植到Scrapy爬蟲ScrapyScrapy爬蟲執行個體編寫/re爬蟲移植
【Python】Scrapy爬蟲介紹&amp;&amp;requests爬蟲移植到Scrapy爬蟲ScrapyScrapy爬蟲執行個體編寫/re爬蟲移植
【Python】Scrapy爬蟲介紹&amp;&amp;requests爬蟲移植到Scrapy爬蟲ScrapyScrapy爬蟲執行個體編寫/re爬蟲移植

進一步修改

spiders/douban.py

檔案

步驟3:編寫 Spider

配置

douban.py

檔案

修改對傳回頁面的處理

修改對新增URL爬取請求的處理

步驟4:配置并發連接配接選項,優化爬取速度:編寫 Pipelines

  • 配置

    pipelines.py

    檔案

    定義對爬取項( Scraped Item)的處理類

    【Python】Scrapy爬蟲介紹&amp;&amp;requests爬蟲移植到Scrapy爬蟲ScrapyScrapy爬蟲執行個體編寫/re爬蟲移植

爬取豆瓣top250的scrapy爬蟲

參考

Python yield 使用淺析

CSS – Python爬蟲常用CSS選擇器(Selectors)

爬蟲Scrapy架構之css選擇器使用

Scrapy:運作爬蟲程式的方式

scrapy css選擇器提取a标簽内href屬性值

douban.py

【Python】Scrapy爬蟲介紹&amp;&amp;requests爬蟲移植到Scrapy爬蟲ScrapyScrapy爬蟲執行個體編寫/re爬蟲移植

使用css選擇器的方法是

選擇href屬性包含

https://movie.douban.com/subject/

這個字元串的所有元素。

import scrapy


class DoubanSpider(scrapy.Spider):
    name = 'douban'
    allowed_domains = ['movie.douban.com/top250']
    start_urls = ['http://movie.douban.com/top250/']

    def decode(self,s):#設定以utf-8編碼解析資料
        return bytes(s,encoding='utf-8')
    def parse(self, response):
        for i in range(25,225,25):
            try:
                url="http://movie.douban.com/top250?start="+str(i)
                yield scrapy.Request(url,callback=self.parse_pages)
                '''
                一個帶有 yield 的函數就是一個 generator,它和普通函數不同,生成一個 generator 
                看起來像函數調用,但不會執行任何函數代碼,直到對其調用 next()(在 for 循環中會自動調用 next())才開始執行。
                雖然執行流程仍按函數的流程執行,但每執行到一個 yield 語句就會中斷,并傳回一個疊代值,下次執行時從 yield 的下一個語句繼續執行
                看起來就好像一個函數在正常執行的過程中被 yield 中斷了數次,每次中斷都會通過 yield 傳回目前的疊代值。
                '''
            except:
                print("wrong page",str(i))
                continue

    def parse_pages(self,response):
        for item in response.css("[href~=https://movie.douban.com/subject/]"):
        #選擇href屬性包含字元串https://movie.douban.com/subject/的标簽,實際上已經直接選中了a标簽
        #for item in response.css(".hd")
            url=item.css("a::attr(href)").extract[0]
            yield scrapy.Request(url,callback=self.parse_detail)

    def parse_detail(self,response):
        total={}
        number=response.css(".top250-no::text").extract[0]
        name=response.css("div>h1::text").extract[0]
        year=response.css(".year::text").extract[0]
        #對year進行更為美觀的處理,使用strip函數去掉開頭和結尾的(和)
        year.strip("()")
        score=response.css(".ll rating_num::text").extract[0]
        total.update({"排名":number,"電影名":name,"上映年份":year,"電影評分":score})
        yield total
           

pipelines處理資料,儲存檔案

import json

class WebScrapyPipeline:
    def process_item(self, item, spider):
        return item

class pagePipeline:
    def open_spider(self,spider):
        self.f=open("information.json","w",encoding='utf-8')

    def close_spider(self, spider):
        self.f.close()

    def process_item(self, item, spider):
        try:
            line = str(json.dumps(dict(item), ensure_ascii=False)) + "\n"
            self.f.write(line)
        except Exception as e:
            print(str(e))
            print("ERROR")
            pass
        return item
           

settings.py設定檔案

  • 請求頭
DEFAULT_REQUEST_HEADERS = {
  'User-Agent' : 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0;',
  'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
  #'Accept-Language': 'en',
}
           
  • 管道
ITEM_PIPELINES = {
   'Web_scrapy.pipelines.WebScrapyPipeline': 300,
   'Web_scrapy.pipelines.pagePipeline':300
}
           

運作爬蟲

示例一:全局運作

示例二:項目級運作

<spider>

是一個 爬蟲程式的名稱——爬蟲類裡面的name屬性(必須required,且在項目中具有唯一性unique)。
【Python】Scrapy爬蟲介紹&amp;&amp;requests爬蟲移植到Scrapy爬蟲ScrapyScrapy爬蟲執行個體編寫/re爬蟲移植

報錯處理

問題一:

修改url池的提取:

問題二:

TypeError: 'method' object is not subscriptable
           

問題解決辦法解釋:

Python TypeError: ‘method’ object is not subscriptable Solution

#錯誤代碼:
url=item.css("a::attr(href)").extract[0]

#正确代碼:
url=item.css("a::attr(href)").extract()[0]
           

問題三:

報錯:

參考解決:

IndexError: list index out of range and python

修正後的

douban.py

版本

直接在F12下copy對應标簽的selector

【Python】Scrapy爬蟲介紹&amp;&amp;requests爬蟲移植到Scrapy爬蟲ScrapyScrapy爬蟲執行個體編寫/re爬蟲移植
import scrapy


class DoubanSpider(scrapy.Spider):
    name = 'douban'
#    allowed_domains = ['movie.douban.com/top250']
    allowed_domains = ['movie.douban.com']
    start_urls = ['http://movie.douban.com/top250/']

    def decode(self,s):#設定以utf-8編碼解析資料
        return bytes(s,encoding='utf-8')
    def parse(self, response):
        #爬取第一頁
        url = "http://movie.douban.com/top250"
        yield scrapy.Request(url, callback=self.parse_pages)
        #爬取後面幾頁
        for i in range(25,250,25):
            try:
                url="http://movie.douban.com/top250?start="+str(i)
                yield scrapy.Request(url,callback=self.parse_pages)
                '''
                一個帶有 yield 的函數就是一個 generator,它和普通函數不同,生成一個 generator
                看起來像函數調用,但不會執行任何函數代碼,直到對其調用 next()(在 for 循環中會自動調用 next())才開始執行。
                雖然執行流程仍按函數的流程執行,但每執行到一個 yield 語句就會中斷,并傳回一個疊代值,下次執行時從 yield 的下一個語句繼續執行
                看起來就好像一個函數在正常執行的過程中被 yield 中斷了數次,每次中斷都會通過 yield 傳回目前的疊代值。
                '''
            except:
                print("wrong page",str(i))
                continue

    def parse_pages(self,response):
        #選擇href屬性包含字元串https://movie.douban.com/subject/的标簽,實際上已經直接選中了a标簽
        #for item in response.css(".hd")
        for item in response.css("div.hd>a"):
            print(item)
            url=item.css("a::attr(href)").extract()[0]
            yield scrapy.Request(url,callback=self.parse_detail)

    def parse_detail(self,response):
        total={}
        number=response.css("span.top250-no::text").extract()[0]
        name=response.css("#content > h1 > span:nth-child(1)::text").extract()[0]
        year=response.css("#content > h1 > span.year::text").extract()[0]
        #對year進行更為美觀的處理,使用strip函數去掉開頭和結尾的(和)
        #year.strip("()")
        #year=year.replace('(','').replace(')','')
        score=response.css("#interest_sectl > div.rating_wrap.clearbox > div.rating_self.clearfix > strong::text").extract()
        total.update({"排名":number,"電影名":name,"上映年份":year,"電影評分":score})
        yield total