scrapy增量爬取


​開始接觸爬蟲的時候還是初學Python的那會,用的還是request、bs4、pandas,再后面接觸scrapy做個一兩個爬蟲,覺得還是框架好,可惜都沒有記錄都忘記了,現在做推薦系統需要爬取一定的文章,所以又把scrapy撿起來。趁着這次機會做一個記錄。

目錄如下:

  • 環境
  • 本地窗口調試命令
  • 工程目錄
  • xpath選擇器
  • 一個簡單的增量爬蟲示例
  • 配置介紹

環境

​自己的環境下安裝scrapy肯定用anaconda(再次強調anaconda的優越性

本地窗口調試與運行

​開發的時候可以利用scrapy自帶的調試功能進行模擬請求,這樣request、response都會與后面代碼保持一樣。

# 測試請求某網站
scrapy shell URL
# 設置請求頭
scrapy shell -s USER_AGENT="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:61.0) Gecko/20100101 Firefox/61.0"  URL

# 指定爬蟲內容輸出文件格式(json、csv等
scrapy crawl SPIDER_NAME -o FILE_NAME.csv

# 創建爬蟲工程
scrapy startproject  articles # 在當前目錄創建一個scrapy工程

新工程結構介紹

# spiders文件下存放所有爬蟲,item.py格式化數據輸出
# middlewares.py 設置請求細節(請求頭之類的),pipelines.py為數據輸出的管道,每一個封裝好的item都會經過這里
# settings.py 對工程進行全局設置(存放配置
├── articles
│   ├── articles
│   │   ├── __init__.py
│   │   ├── items.py
│   │   ├── middlewares.py
│   │   ├── pipelines.py
│   │   ├── settings.py
│   │   └── spiders
│   │       ├── healthy_living.py
│   │       ├── __init__.py
│   │       └── people_health.py
│   └── scrapy.cfg
├── README.en.md
└── README.md

頁面解析神器——Xpath選擇器

​ scrapy自帶xpath選擇器,很方便,簡單介紹一些常用的

# 全站爬取神器--LinkExtractor,可以自動獲取該標簽下的所有url跟text(因為網站結構大都一個套路
from scrapy.linkextractors import LinkExtractor
le = LinkExtractor(restrict_xpaths="//ul[@class='nav2_UL_1 clearFix']")# 返回一個迭代器,通過循環(for i in le),可獲取url(i.url)  (i.text)

# 獲取屬性class為所有aa的div標簽內容中的內容
response.xpath("//div[@class='aa']/text()").extract()		# '//'代表獲取所有,'/'代表獲取第一個,類似的可以找屬性為ul的其它標簽

# 獲取內容包含“下一頁”的所有a標簽中包含的鏈接(提取下一頁鏈接神器
response.xpath("//a[contains(text(),'下一頁')]/@href").extract()

一個簡單的增量爬取示例

​這里增量爬取的思想很簡單:目標網站的數據都是按照時間排列的,所以在對某個連接進行request之前,先查詢數據庫中有沒有這條數據,如果有,就停止爬蟲,如果沒有發起請求

class HealthyLiving(scrapy.Spider):
    # 一定要一個全局唯一的爬蟲名稱,命令行啟動的時候需要指定該名稱
    name = "healthy_living"
    # 指定爬蟲入口,scrapy支持多入口,所以一定是lis形式
    start_urls = ['http://www.jkb.com.cn/healthyLiving/']

    '''
    抓取大類標簽入口
    '''
    def parse(self, response):
        le = LinkExtractor(restrict_xpaths="//ul[@class='nav2_UL_1 clearFix']")
        for link in le.extract_links(response)[1:-1]:
            tag = link.text
            # 將這一級提取到的信息,通過請求頭傳遞給下一級(這里是為了給數據打標簽
            meta = {"tag": tag}
            # 依次解析每一個鏈接,並傳遞到下一級進行繼續爬取
            yield scrapy.Request(link.url, callback=self.parse_articles, meta=meta)

    '''
    抓取頁面內的文章鏈接及下一頁鏈接
    '''
    def parse_articles(self, response):
        # 接收上一級傳遞的信息
        meta = response.meta
        article_links = response.xpath("//div[@class='txt']/h4/a/@href").extract()
        for link in article_links:
            res = self.collection.find_one({"article_url": link}, {"article_url": 1})
            full_meta = dict(meta)
            # 將文章鏈接傳入下一級
            full_meta.update({"article_url": link})
            if res is None:
                yield scrapy.Request(link, callback=self.parse_article, meta=full_meta)
            else:
                return
        next_page = response.xpath("//div[@class='page']//a[contains(text(),'»')]/@href").extract()[0]
        if next_page:
            yield scrapy.Request(next_page, callback=self.parse_articles, meta=meta)

# 最后解析頁面,並輸出
    def parse_article(self, response):
     # 從item.py中導入數據封裝格式
        article_item = ArticlesItem()
        meta = response.meta
        # 利用xpath提取頁面信息並封裝成item
        try:
            article_item["tag"] = ""
            #  ... 省略
        finally:
            yield article_item

工程配置介紹

​設置請求頭、配置數據庫

# 設置請求頭,在middlewares.py中設定,在settings.py中啟用
class RandomUA(object):
    user_agents = [
            "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit"
            "/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36",
            "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11",
            "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit"
            "/534.16 (KHTML, like Gecko) Chrome/10.0.648.133 Safari/534.16"
        ]

    def process_request(self, request, spider):
        request.headers["User-Agent"] = random.choice(self.user_agents)


# 設置數據入庫處理,在pipeline.py進行配置,在settings.py進行啟用
class MongoPipeline(object):
    def __init__(self, mongo_uri, mongo_db):
        self.mongo_uri = mongo_uri
        self.mongo_db = mongo_db

    @classmethod
    def from_crawler(cls, crawler):
        return cls(
            mongo_uri=crawler.settings.get('MONGO_URI'),
            mongo_db=crawler.settings.get('MONGO_DB')
        )

    def open_spider(self, spider):
        print("開始爬取", datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
        self.client = pymongo.MongoClient(self.mongo_uri)
        self.db = self.client[self.mongo_db]

    def process_item(self, item, spider):
        data = self.db[item.collection].find_one({"title": item["title"], "date": item["date"]})

        if data is None:
            self.db[item.collection].insert(dict(item))
        # else:
        #     self.close_spider(self, spider)
        return item

    def close_spider(self, spider):
        print("爬取結束", datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
        self.client.close()
# 在settings.py啟動:請求頭的修改,數據庫的配置
DOWNLOADER_MIDDLEWARES = {
   # 'articles.middlewares.ArticlesDownloaderMiddleware': 543,
   'articles.middlewares.RandomUA': 543,# 543代表優先級,數字越低優先級越高
}

ITEM_PIPELINES = {
   'articles.pipelines.MongoPipeline': 300,
}


# 一些其它配置
ROBOTSTXT_OBEY = True # 是否遵守網站的robot協議
FEED_EXPORT_ENCODING = 'utf-8' # 指定數據輸出的編碼格式
## 數據庫配置
MONGO_URI = ''
MONGO_DB = ''
MONGO_PORT = 27017
MONGO_COLLECTION = ''


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM