【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爬蟲架構結構
爬蟲架構
爬蟲架構是實作爬蟲功能的一個軟體結構和功能元件集合。
爬蟲架構是一個半成品,能夠幫助使用者實作專業網絡爬蟲。
五個主要子產品,兩個中間件。
Engine(不需要使用者修改)
- 控制所有子產品之間的資料流
- 根據條件觸發事件
Downloader
- 根據請求下載下傳網頁
- 不需要使用者修改
Scheduler
- 不需要使用者修改
- 對所有爬取請求進行排程管理
Spider(最核心)
- 解析Downloader傳回的響應(Response)
- 産生爬取項(Scraped item)
- 産生額外的爬取請求(Request)
Item Pipelines
- 以流水線處理Spider的爬取項
- 由一組操作順序組成,類似一個流水線,每個操作是一個Item Pipeline類型。
- 可能操作包括:清理,檢驗和查重爬取項中的HTML資料,将資料儲存到資料庫。
中間件:Downloader Middleware
-
目的:實施 Engine、 Scheduler和 Downloader之間進行使用者可配置的控制
功能:修改、丢棄、新增請求或響應
Spider Middleware
-
目的:對請求和爬取項的再處理
功能:修改、丢棄、新增請求或爬取項
使用者可以編寫配置代碼
requests和Scrapy對比
Scrapy常用指令
Scrapy 爬蟲基本使用
第一個Scrapy執行個體
DemoSpider這個類叫什麼名字無所謂,隻要他是繼承于scrapy.Spider的子類即可。
parse()用于處理響應,解析内容形成字典,發現新的URL爬取請求。
産生步驟
- 步驟1:建立一個 Scrapy爬蟲工程
- 步驟2:在工程中産生一個 Scrapy爬蟲
- 步驟3:配置産生的 spider爬蟲
- 步驟4:運作爬蟲,擷取網頁。
cd pycodes
scrapy startproject python123demo
cd python123demo
scrapy genspider demo python123.io#生成一個名稱為demo的spider,生成了demo.py
yield關鍵字。生成器。生成器是一個不斷産生值的函數。
包含 yield語句的函數是一個生成器
生成器每次産生一個值( yield語句),函數被當機,被喚醒後再産生一個值。
生成器,産生小于n的數的平方值。并将所有的傳回值傳回給上層調用函數。
普通寫法:存儲所有的數。
生成器寫法:每次隻要一個值。每次的存儲空間是一個的存儲空間。
當n很大時,生成器就有很大的優勢。
Scrapy爬蟲的基本使用
Scrapy爬蟲的使用步驟
- 步驟1:刨建一個工程和 Spider模闆
- 步驟2:編寫 Spider
- 步驟3:編寫 Item Pipeline
- 步驟4:優化配置政策
Scrap爬蟲的資料類型
- Request類
- Response類
- Item類
Request類
class scrapy.http.Request()
Request對象表示ー個HTTP請求。
由 Spider生成,由 Downloader.執行。
Response類
class scrapy.http.Response()
Response對象表示一個HTTP響應。
由 Downloader生成,由 Spider處理。
Item類
class scrap.item.Item()
Item對象表示一個從HTML頁面中提取的資訊内容。
由 Spider生成,由 Item Pipelines處理。
爬取資訊,封裝成字典,存儲。
Scrapy爬蟲支援多種HTML資訊提取方法
- Beautiful Soup
- lxml
- re
- Xpath Selector
- CSS Selector
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(' ','',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,自己在自己喜歡的路徑下建立一個項目
步驟2:建立Spider.模闆
scrapy startproject Web_scrapy
cd Web_scrapy
scrapy genspider douban movie.douban.com/top250
進一步修改
spiders/douban.py
檔案
步驟3:編寫 Spider
配置
douban.py
檔案
修改對傳回頁面的處理
修改對新增URL爬取請求的處理
步驟4:配置并發連接配接選項,優化爬取速度:編寫 Pipelines
- 配置
pipelines.py
檔案
定義對爬取項( Scraped Item)的處理類
爬取豆瓣top250的scrapy爬蟲
參考
Python yield 使用淺析
CSS – Python爬蟲常用CSS選擇器(Selectors)
爬蟲Scrapy架構之css選擇器使用
Scrapy:運作爬蟲程式的方式
scrapy css選擇器提取a标簽内href屬性值
douban.py
使用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)。
報錯處理
問題一:
修改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
版本
douban.py
直接在F12下copy對應标簽的selector
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