爬蟲框架之Scrapy(二)
遞歸解析
糗事百科遞歸解析
在前面的例子裏只是爬取了糗事百科熱門的第一個頁面,但是當我們需要爬取更多的頁面時,需要對每個頁面的url依次發起請求,然後通過解析的方法進行作者和標題的解析。
我們可以構建一個url列表,放進去所有頁面的url,但是這樣是不推薦的。
我們也可以通過requests的方法來手動解析,然後來使用遞歸的思想來編寫程序。
比如:
糗事百科首頁的頁碼
# -*- coding: utf-8 -*-
import scrapy
from happy.items import HappyItem
class QiushiSpider(scrapy.Spider):
name = ‘qiushi‘
allowed_domains = [‘www.qiushibaike.com‘]
start_urls = [‘http://www.qiushibaike.com/‘]
# 爬取多個頁面
pageNum = 1
url = ‘https://www.qiushibaike.com/8hr/page/%d/‘
def parse(self, response):
# xpath為response中的方法
odiv = response.xpath(‘//div[@class="recommend-article"]//li ‘)
for li in odiv:
# 連續的li裏面混有廣告,屏蔽掉廣告
try:
title = li.xpath(‘.//div[@class="recmd-right"]/a/text()‘)[0].extract()
author = li.xpath(‘.//span[@class="recmd-name"]/text()‘)[0].extract()
except:
continue
# 提供item到管道文件
item = HappyItem()
item[‘title‘] = title
item[‘author‘] = author
yield item
# 爬取所有頁碼數據
if self.pageNum <= 13:
self.pageNum += 1
url = self.url%(self.pageNum)
# 遞歸爬取數據 發起URL請後,將得到的數據進行使用parse解析,所以要使用回調函數callback
yield scrapy.Request(url=url, callback=self.parse)
得到結果:
post請求發送
Scrapy不用手動對起始列表中的url發送請求,是因為爬蟲文件中爬蟲類繼承了Spider父類中start_requests(self)方法,該方法就可以對初始列表中的url發起請求。
def start_requests(self):
for u in self.start_urls:
yield scrapy.Request(url=u,callback=self.parse)
但是該方法默認實現的是get請求,如果需要發起post請求,則需要子類重寫該方法
def start_requests(self):
#請求的url
post_url = ‘http://fanyi.baidu.com/sug‘
# post請求參數
formdata = {
‘kw‘: ‘wolf‘,
}
# 發送post請求
yield scrapy.FormRequest(url=post_url, formdata=formdata, callback=self.parse)
Scrapy的日誌等級
在使用Scrapy crawl spidername 運行程序時,在終端裏打印輸出的就是Scrapy的日誌。
一般的日誌種類:
ERROR:錯誤
WARNING:警告
INFO:一般的信息
DEBUG:調試的信息
設置日誌的指定輸出:
在settings.py配置文件中加入:
LOG_LEVEL = 日誌的指定種類
LOG_FILE = ‘log.txt’ 表示將日誌寫入指定文件
請求傳參
有時候我們爬取的數據不在同一個頁面,比如在這個頁面的下級頁面,這時候我們就需要用法請求傳參。
例子:https://www.cnblogs.com/xiaozx/p/10744604.html 爬取網易新聞的國內版 srapy
提高scrapy爬取效率的技巧
1 增加並發:
默認scrapy開啟的並發線程為32個,可以適當進行增加。在settings配置文件中修改CONCURRENT_REQUESTS = 100值為100,並發設置成了為100。
2 降低日誌級別:
在運行scrapy時,會有大量日誌信息的輸出,為了減少CPU的使用率。可以設置log輸出信息為INFO或者ERROR即可。在配置文件中編寫:LOG_LEVEL = ‘INFO’.
3 禁止cookie
如果不是真的需要cookie,則在scrapy爬取數據時可以進制cookie從而減少CPU的使用率,提升爬取效率。在配置文件中編寫:COOKIES_ENABLED = False.
4 禁止重試
對失敗的HTTP進行重新請求(重試)會減慢爬取速度,因此可以禁止重試。在配置文件中編寫:RETRY_ENABLED = False.
5 減少下載超時
如果對一個非常慢的鏈接進行爬取,減少下載超時可以能讓卡住的鏈接快速被放棄,從而提升效率。在配置文件中進行編寫:DOWNLOAD_TIMEOUT = 10 超時時間為10s.
示例
爬取校花網校花圖片 www.521609.com
# -*- coding: utf-8 -*-
import scrapy
from xiaohua.items import XiaohuaItem
class XiahuaSpider(scrapy.Spider):
name = ‘xiaohua‘
allowed_domains = [‘www.521609.com‘]
start_urls = [‘http://www.521609.com/daxuemeinv/‘]
pageNum = 1
url = ‘http://www.521609.com/daxuemeinv/list8%d.html‘
def parse(self, response):
li_list = response.xpath(‘//div[@class="index_img list_center"]/ul/li‘)
for li in li_list:
school = li.xpath(‘./a/img/@alt‘).extract_first()
img_url = li.xpath(‘./a/img/@src‘).extract_first()
item = XiaohuaItem()
item[‘school‘] = school
item[‘img_url‘] = ‘http://www.521609.com‘ + img_url
yield item
if self.pageNum < 10:
self.pageNum += 1
url = format(self.url % self.pageNum)
#print(url)
yield scrapy.Request(url=url,callback=self.parse)
pipelines.py
import json
import os
import urllib.request
class XiaohuaPipeline(object):
def __init__(self):
self.fp = None
def open_spider(self,spider):
print(‘開始爬蟲‘)
self.fp = open(‘./xiaohua.txt‘,‘w‘)
def download_img(self,item):
url = item[‘img_url‘]
fileName = item[‘school‘]+‘.jpg‘
if not os.path.exists(‘./xiaohualib‘):
os.mkdir(‘./xiaohualib‘)
filepath = os.path.join(‘./xiaohualib‘,fileName)
urllib.request.urlretrieve(url,filepath)
print(fileName+"下載成功")
def process_item(self, item, spider):
obj = dict(item)
json_str = json.dumps(obj,ensure_ascii=False)
self.fp.write(json_str+‘\n‘)
#下載圖片
self.download_img(item)
return item
def close_spider(self,spider):
print(‘結束爬蟲‘)
self.fp.close()
重點是settings.py 設置有利於爬蟲下載的方式
USER_AGENT = ‘Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36‘
# Obey robots.txt rules
ROBOTSTXT_OBEY = False
# Configure maximum concurrent requests performed by Scrapy (default: 16)
CONCURRENT_REQUESTS = 100
COOKIES_ENABLED = False
LOG_LEVEL = ‘ERROR‘
RETRY_ENABLED = False
DOWNLOAD_TIMEOUT = 3
# Configure a delay for requests for the same website (default: 0)
# See https://doc.scrapy.org/en/latest/topics/settings.html#download-delay
# See also autothrottle settings and docs
# The download delay setting will honor only one of:
#CONCURRENT_REQUESTS_PER_DOMAIN = 16
#CONCURRENT_REQUESTS_PER_IP = 16
DOWNLOAD_DELAY = 3
UA池和代理池
下載中間件
下載中間件,是位於引擎和下載器之間的一組層組件。
作用:
1 引擎將請求傳遞給下載器的過程中,下載中間件可以對請求進行一些列的處理,比如設置user-agent,設置代理等。
2 在下載器完成將response傳遞給引擎中,下載中間件可以對響應進行一些列處理,比如進行gzip解壓等。
我們使用下載中間件,一般會對請求隨機的設置user-agent,設置隨機的代理,目的在於應對網站的反爬策略。
ua池
盡可能多將scrapy工程的請求偽裝成不同類型的瀏覽器身份。
流程:
1 在下載中間件中攔截請求
2 將攔截到的請求信息中的UA進行篡改
3 在配置文件中開啟下載中間件
import random
from scrapy.contrib.downloadermiddleware.useragent import UserAgentMiddleware
# 單獨給ua池一個下載中間件的一個類
class RandomUserAgent(UserAgentMiddleware):
def process_request(self, request, spider):
user_agent_list = [
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 "
"(KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1",
"Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 "
"(KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 "
"(KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6",
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 "
"(KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6",
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 "
"(KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 "
"(KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5",
"Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 "
"(KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 "
"(KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 "
"(KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24",
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 "
"(KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24"
]
# 隨機挑選一個user-agent
ua = random.choice(user_agent_list)
# 將他寫入到當前攔截到的request請求中
request.headers.setdefault(‘User-Agent‘, ua)
settings.py
DOWNLOADER_MIDDLEWARES = {
‘happy2.middlewares.RandomUserAgentMiddleware‘: 400,
}
代理池
盡可能多將scrapy工程的請求設置成不同的ip訪問。
流程:
1 在下載中間件中攔截請求
2 將攔截到的請求ip改成某一代理ip
3 在配置文件中開啟下載中間件
# 還是單獨寫一個類
class ProxyMiddleware(object):
def process_request(self, request, spider):
# http型的可用ip
PROXY_http = [
‘153.180.102.104:80‘,
‘195.208.131.189:56055‘,
]
# 這是https請求類型的可用IP
PROXY_https = [
‘120.83.49.90:9000‘,
‘95.189.112.214:35508‘,
]
# 取出本次請求的路徑
h = request.url.split(‘:‘)[0]
if h == ‘http‘:
ip = random.choice(PROXY_http)
request.meta[‘proxy‘] = ‘http://‘ + ip
elif h == ‘https‘:
ip = random.choice(PROXY_https)
request.meta[‘proxy‘] = ‘https://‘ + ip
settings.py
DOWNLOADER_MIDDLEWARES = {
‘happy2.middlewares.ProxyMiddleware‘:400,
}
爬蟲框架之Scrapy(二)