Scrapy(一) 入門

之前大部分的中間件都是在Docker中做的, 感覺Docker的確是牛逼, 隔離環境. 最近做一個爬蟲的項目, 用到Scrapy, 最開始沒有往Docker方面想, 之後有空需要研究下Docker如何安裝Scrapy

安裝

Scrapy是基於Python的爬蟲框架, 需要先安裝Python, 我的環境是Ubuntu 16.4默認條件下, Ubuntu安裝了兩個Python, Python2.7和Python3.5, 不知道這是什麼操作

目前最新的Scrapy是基於Python3的, 如果在機器上有兩個版本的Python, Scrapy會有衝突, 所以最開始先卸載Python2.7

sudo apt-get remove python

scrapy需要通過pip3進行安裝, 注意這裏是要pip3安裝scrapy, 不是用pip, pip是基於python2的

安裝pip3

可以使用如下命令安裝pip3

sudo apt-get install python3-pip

如果安裝過程中出現任何錯誤, 就多試幾遍

查看pip3是否安裝成功

pip3 –version

安裝scrapy

pip3 install scrapy

安裝過程中可能會報錯, 並提示更新pip3

You are using pip version 8.1.1, however version 19.3.1 is available.

You should consider upgrading via the 'pip install --upgrade pip' command.

使用如下的命令更新pip3

pip3 install --upgrade pip

繼續通過pip3 install scrapy, 出現下面的錯誤

這是因爲將pip3更新爲10.0.0後庫裏面的函數有所變動造成這個問題

nano /usr/bin/pip3

將原來的

from pip import main

if __name__ == '__main__':

    sys.exit(main())

改爲

from pip import __main__

if __name__ == '__main__':

    sys.exit(__main__._main())

 

直到這裏, 再通過pip3安裝scrapy, 就基本上不會出錯了

 

通過下面的命令驗證scrapy的安裝

pip3 list | grep Scrapy

案例

找了很多網上的教程, 發現有一個例子比較適合入門

scrapy startproject 工程名

通過這個命令, scrapy會幫忙建一個例子工程

items.py

這個文件定義了工程中會用到的類

import scrapy


class CrawlsHouseItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    pass
class DanKe(CrawlsHouseItem):
     # 房子名稱
    host_name = scrapy.Field()
    # 租金
    price = scrapy.Field()
    # room list
    room_list = scrapy.Field()

pipelines.py文件定義了工程爬蟲的輸出

第一種方法是輸出到文件

import io
import sys
reload(sys)
sys.setdefaultencoding('utf8')

class TutorialPipeline(object):
    def process_item(self, item, spider):
        return item
class CrawlsHousePipeline(object):
    def __init__(self):     
        self.file = io.open('danke.json', 'a', encoding='utf-8')

    def __del__(self):
        self.file.close()

    def process_item(self, item, spider):
        print(item, spider)       
        self.file.writelines(item['host_name']+'|'+item['price']+'|'+item['room_list'])
        return item

第二種方式是輸出到數據庫

import io
import pymysql
import sys
reload(sys)
sys.setdefaultencoding('utf8')


class TutorialPipeline(object):
    def process_item(self, item, spider):
        return item
class CrawlsHousePipeline(object):
    def __init__(self):

    	self.conn = pymysql.connect(
            host='192.168.25.132',
            port=3306,
            database='dk',
            user='root',
            password='123456',
            charset='utf8'
    	)
    	self.cursor = self.conn.cursor()

    def process_item(self, item, spider):
	sql = ("insert into danke(host_name,price,room_list)"
        	   "values (%s, %s, %s)")

   	item = dict(item)

    	data = [item['host_name'],
        	item['price'],
           	item['room_list']
               ]
   	self.cursor.execute(sql, data)
   	self.conn.commit()

    	return item

    def close_spider(self, spider):
        self.cursor.close()
        self.conn.close()

其他還有文件middlewares.py定義了工程中要用到的中間件

文件settings.py定義了工程的設置

真正的爬蟲文件在spiders裏面

class DankeSpider(scrapy.Spider):
    
    name = 'danke'
    
    allowed_domains = ['www.danke.com']
    
    start_urls = ['https://www.danke.com/room/bj']

    def parse(self, response):
        
        for href in response.xpath("//div[@class='r_lbx_cena']//a//@href").extract():
            
            yield scrapy.Request(href, self.parse_item)
        
        for next_href in response.xpath("//div[@class='page']//a//@href").extract():
            yield scrapy.Request(next_href)

    def parse_item(self, response):
        
        danke = DanKe()
        
        danke['host_name'] = response.xpath('//h1//text()').extract()[0]
        
        price = response.xpath('//div[@class="room-price-sale"]//text()').extract()[0]
        danke['price'] = price.replace(" ", "").replace("\n", "")
        
        room_list = []
        for room in response.xpath('//div[@class="room-list"]//text()').extract():
            room = room.replace(" ", "").replace("\n", "")
            if len(room) != 0:
                room_list.append(room)
        danke["room_list"] = ",".join(room_list)
        
        return danke

分頁的抓取

分頁的抓取是個比較頭痛的部分, 最簡單的情況是在代碼中會直接有下一個頁面的地址

<div class="page">
    <a href="https://www.danke.com/room/bj?page=1"  class="on">1</a>
    <a href="https://www.danke.com/room/bj?page=2"  class="">2</a>
    <a href="https://www.danke.com/room/bj?page=3"  class="">3</a>
    <a href="https://www.danke.com/room/bj?page=4"  class="">4</a>
    <a href="https://www.danke.com/room/bj?page=5"  class="">5</a>
    <a href="https://www.danke.com/room/bj?page=2"> > </a>
</div>

這樣的話, 可以直接在scrapy進行循環獲取

for next_href in response.xpath("//div[@class='page']//a//@href").extract():
            yield scrapy.Request(next_href)

但是大部分頁面的分頁都是通過js控制的, 並不會有直接的鏈接, 這個時候如果只是單一系列的頁面, 可以定義全局的變量, 然後拼頁面的鏈接實現

 

如果是一系列的頁面, 不太容易可以給全局變量進行清零, 所以這種方式不合適

 

另外一種比較靈活的辦法, 分頁的控件都會有下一頁的按鈕, 基本上都會傳入下一頁的頁碼, 這時候通過解析錨點得到下一頁的頁碼, 再拼頁面的鏈接實現下一頁的抓取

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章