1. 程式人生 > >爬蟲框架之Scrapy(二)

爬蟲框架之Scrapy(二)

org line txt ebs same wow64 ocs download request請求

遞歸解析

糗事百科遞歸解析

在前面的例子裏只是爬取了糗事百科熱門的第一個頁面,但是當我們需要爬取更多的頁面時,需要對每個頁面的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(二)