Python 萌新 - 花10分鐘學爬蟲

xietao3發表於2018-01-26

Python 新手入門很多時候都會寫個爬蟲練手,本教程使用 Scrapy 框架,幫你簡單快速實現爬蟲,並將資料儲存至資料庫。在機器學習中資料探勘也是十分重要的,我的資料科學老師曾經說過,好演算法不如好資料。

介紹

Scrapy ,Python 開發的一個快速、高層次的螢幕抓取和 web 抓取框架,用於抓取 web 站點並從頁面中提取結構化的資料。檔案結構清晰,即使是小白也能夠快速上手,總之非常好用?。

XPath ,它是一種用來查詢 XML 文件中節點位置的語言。 XPath 基於 XML 的樹狀結構,有不同型別的節點,包括元素節點,屬性節點和文字節點,提供在資料結構樹中找尋節點的能力。

MySQL 是一種關聯式資料庫管理系統,它的優勢:它是免費的。作者是下載了 MAMP for Mac ,內嵌MySQLApache

首先通過 Scrapy 爬取到網頁後, 通過 XPath 來定位指定元素,獲取到你想要的資料,得到資料之後可以將資料存入資料庫( MySQL )。簡單瞭解之後就可以開始編寫你的爬蟲。

*重要:下載並檢視 Demo ,結合本文可以快速實現一個基本爬蟲✌️。

準備工作

安裝 Scrapy (系統預設安裝了 Python):

$ pip install Scrapy
複製程式碼

在當前目錄新建工程

$ scrapy startproject yourproject
複製程式碼

新建工程檔案結構如下:

yourproject/
----|scrapy.cfg             # 部署配置檔案
    |yourproject/           # 工程目錄
    |----__init__.py
    |----items.py           # 專案資料檔案
    |----pipelines.py       # 專案管道檔案
    |----settings.py        # 專案設定檔案
    |----spiders/           # 我們的爬蟲 目錄
        |----__init__.py    # 爬蟲主要程式碼在這裡    
複製程式碼

簡單的爬蟲主要使用了spidersitemspipelines這三個檔案:

  • spider :爬蟲的主要邏輯。
  • items :爬蟲的資料模型。
  • pipelines : 爬蟲獲取到的資料的加工工廠,可以進行資料篩選或儲存。

資料模型:items

items

先看看我們要爬取的網站,這個是 Scrapy 官方 Demo 爬取資料用的網站,我們先用這個來練手。

quotes

分析網頁的資訊我們可以看到網頁主體是一個列表,列表每一行都包含可一句引用、作者名字、標籤等資訊。作者名右邊點選(about)可以看到作者的詳細資訊,包括介紹、出生年月日、地點等等。根據上面的資料,我們可以先建立如下資料模型:

items.py

import scrapy

# quote 我們要爬取的主體
class QuoteItem(scrapy.Item):
    text = scrapy.Field()
    tags = scrapy.Field()
    author = scrapy.Field()

    next_page = scrapy.Field()
    pass
    
# quote 的作者資訊 對應 QuoteItem.author
class AuthorItem(scrapy.Item):
    name = scrapy.Field()
    birthday = scrapy.Field()
    address = scrapy.Field()
    description = scrapy.Field()
    pass
複製程式碼

所有的模型必須繼承scrapy.Item,完成這一步我們就可以開始寫爬蟲的邏輯了。

# 完整的 QuoteItem 資料結構示例
{
    text,
    tags,
    author:{
        name,
        birthday,
        address,
        description
    }
}
複製程式碼

爬蟲:spider

Spider

既然是爬蟲,自然需要去爬取網頁,爬蟲部分的幾個要點:

  1. 引入你建立的資料模型
  2. 首先爬蟲類要繼承scrapy.Spider
  3. 設定爬蟲的名字name,啟動爬蟲時要用。
  4. 將你要爬取的網址放入start_requests(),作為爬蟲的起點。
  5. 爬取的網頁資訊成功後,在的請求響應parse()中解析。

spiders/init.py

  • 在頂部引入建立的資料模型。
import scrapy
from ScrapySample.items import QuoteItem
from ScrapySample.items import AuthorItem
複製程式碼
  • 爬蟲類,name->爬蟲名字,allowed_domains->爬取網頁的白名單。
class QuotesSpider(scrapy.Spider):
    name = "quotes"
    allowed_domains = ["quotes.toscrape.com"]
複製程式碼
  • start_requests()中記錄你要爬取的網址。

    可以只放入一個網址,然後讓爬蟲自己爬取起始網址中下一頁的連結。也可以在這裡把所有需要爬的網址直接放入,比如說page一般是從1開始,並且有序的,寫一個for迴圈可以直接輸入所有頁面的網址。

    本文使用的是讓爬蟲自己去爬取下一頁網址的方式,所以只寫入了一個起始網址。

    def start_requests(self):
        urls = [
            'http://quotes.toscrape.com/page/1/',
        ]
        for url in urls:
            yield scrapy.Request(url=url, callback=self.parse)
複製程式碼
  • 如下程式碼,爬取網頁成功之後,我們要分析網頁結構,找到我們需要的資料。

    我們先來看XPath語法,//div[@class="col-md-8"]/div[@class="quote":這是表示查詢 class 為"col-md-8"的 div 節點下的一個子節點,並且子節點是一個 class 為"quote" div 節點。如果在當前頁面找到了這樣一個節點,則返回節點資訊,如果沒有找到則返回None

    def parse(self, response):
        # 通過檢視器,找到列表所在的節點
        courses = response.xpath('//div[@class="col-md-8"]/div[@class="quote"]')

        for course in courses:
            # 將資料模型例項化 並從節點中找到資料填入我們的資料模型
            item = QuoteItem()
            # 輪詢 course 節點下所有 class 為 "text" 的 span 節點,獲取所有匹配到的節點的 text() ,由於獲取到的是列表,我們預設取第一個。
            item['text'] = course.xpath('.//span[@class="text"]/text()').extract_first()
            item['author'] = course.xpath('.//small[@class="author"]/text()').extract_first()
            item['tags'] = course.xpath('.//div[@class="tags"]/a/text()').extract()

            # 請求作者詳細資訊
            author_url = course.xpath('.//a/@href').extract_first()
            # 如果作者介紹的連結不為空 則去請求作者的詳細資訊
            if author_url != '':
                request = scrapy.Request(url='http://quotes.toscrape.com'+author_url, dont_filter=True, callback=self.authorParse)
                # 將我們已經獲取到的 QuoteItem 傳入該請求的回撥函式 authorParse(),在該函式內繼續處理作者相關資料。
                request.meta['item'] = item
                yield request
        
        # 繼續爬向下一頁 該函式具體實現下面會分析
        next_page_request = self.requestNextPage(response)
        yield next_page_request
複製程式碼

這段註釋不是很詳細,如果看不懂可能需要補一下相關知識。

  • 爬取作者詳細資訊

    成功獲取作者詳細資訊 AuthorItem 後並且賦值給 QuoteItem 的屬性 author ,這樣一個完整的引述資訊 QuoteItem 就組裝完成了。

    def authorParse(self, response):
        # 先獲取從 parse() 傳遞過來的 QuoteItem
        item = response.meta['item']
        # 通過檢視器,找到作者詳細資訊所在節點
        sources = response.xpath('//div[@class="author-details"]')
        
        # 例項化一個作者資訊的資料模型
        author_item = AuthorItem()
        # 往作者資訊模型填入資料
        for source in sources:
            author_item['name'] = source.xpath('.//h3[@class="author-title"]/text()').extract_first()
            author_item['birthday'] = source.xpath('.//span[@class="author-born-date"]/text()').extract_first()
            author_item['address'] = source.xpath('.//span[@class="author-born-location"]/text()').extract_first()
            author_item['description'] = source.xpath('.//div[@class="author-description"]/text()').extract_first()
    
        # 最後將作者資訊 author_item 填入 QuoteItem 
        item['author'] = author_item
        # 儲存組裝好的完整資料模型
        yield item
複製程式碼
  • 爬蟲自己找到出路(下一頁網頁連結)

    通過檢視器我們可以找到下一頁按鈕元素,找到該節點並提取連結,爬蟲即奔向下一個菜園。

    def requestNextPage(self, response):
        next_page = response.xpath('.//li[@class="next"]/a/@href').extract_first()
        # 判斷下一個是按鈕元素的連結是否存在
        if next_page is not None:
            if next_page != '':
                return scrapy.Request(url='http://quotes.toscrape.com/'+next_page, callback=self.parse)
        return None
複製程式碼

爬蟲的主要邏輯到這裡就結束了,我們可以看到,一小段程式碼就可以實現一個簡單的爬蟲。一般主流網頁都針對防爬蟲做了一些處理,實操過程中也許並不會這麼順利,我們可能需要模仿瀏覽器的User-Agent,或者做訪問延時防止請求過於頻繁等等處理。

資料處理:pipelines

pipelines是 Scrapy 用來後續處理的管道,可以同時存在多個,並且可以自定義順序執行,通常用來做資料處理和資料儲存。我們需要在settings.py檔案中設定需要需要執行的管道和執行順序。

# 在 settings.py 加入下面的程式碼
ITEM_PIPELINES = {
   'ScrapySample.pipelines.ScrapySamplePipeline': 300,
}
複製程式碼

在這裡我只使用了一個管道ScrapySamplePipeline,用來將資料儲存到資料庫當中,後面的數字300是表示該管道的優先順序,數字越小優先順序越高。

由於我們要儲存資料到資料庫,所以我們需要先在本地搭建起資料庫服務,我這裡用的是MySQL,如果沒有搭建的小夥伴可以下個 MAMP 免費版本,安裝好傻瓜式操作一鍵啟動ApacheMySQL服務。當然,資料庫和表還是要自己建的。

MAMP

# 在 pipelines.py 中加入資料庫配置資訊
config = {
    'host': '127.0.0.1',
    'port': 8081,
    'user': 'root',
    'password': 'root',
    'db': 'xietao',
    'charset': 'utf8mb4',
    'cursorclass': pymysql.cursors.DictCursor,
}
複製程式碼

我們可以在__init__()函式裡做一些初始化工作,比如說連線資料庫。

然後process_item()函式是管道處理事件的函式,我們要在這裡將資料儲存入資料庫,我在這個函式裡寫了一些插入資料庫操作。

close_spider()函式是爬蟲結束工作時候呼叫,我們可以在這裡關閉資料庫。

class ScrapySamplePipeline(object):

    def __init__(self):
        # 連線資料庫
        self.db = sql.connect(**config)
        self.cursor = self.db.cursor()

    def process_item(self, item, spider):
        # 先儲存作者資訊
        sql = 'INSERT INTO author (name, birthday, address, detail) VALUES (%s, %s, %s, %s)'
        self.cursor.execute(sql, (item['author']['name'], item['author']['birthday'], item['author']['address'], item['author']['description']))
        # 獲取作者id
        author_id = self.cursor.lastrowid

        # 儲存引述資訊
        sql = 'INSERT INTO spider (text, tags, author) VALUES (%s, %s, %s)'
        self.cursor.execute(sql, (item['text'], ','.join(item['tags']), author_id))
        self.db.commit()

    # 即將結束爬蟲
    def close_spider(self, spider):
        self.db.close()
        self.cursor.close()
        print('close db')
複製程式碼

如果不需要儲存資料庫或者對資料處理的話,pipelines這部分是可以忽略的。這個時候在命令列切換到工程目錄下,輸入開始執行爬蟲命令:

$ scrapy crawl quotes
複製程式碼

部分不儲存到資料庫的小夥伴可以使用下方命令,將爬取的資料以 Json 格式匯出到該工程目錄下。

$ scrapy crawl quotes -o quotes.json
複製程式碼

最後貼上資料庫資料成功錄入的截圖。

Data

總結

這是作者最開始學習 Python 的時候寫的,有一些不盡人意的地方後面會再調整,寫下本文用意是鞏固知識或是用於以後回顧,同時希望對同樣剛開始學習 Python 的讀者有所幫助。

最後再次貼上Demo ️。

相關文章