[Python] CrawlSpider框架爬取數(shù)碼寶貝全圖鑒

寫在開頭:本文運行爬蟲的示例網(wǎng)站為 數(shù)碼獸數(shù)據(jù)庫http://digimons.net/digimon/chn.html

文章提及的細節(jié)參見我的另外三篇簡文
MySQL本地類細節(jié)配置:
[Python] 爬取bioinfo帖子相關信息 (requests + openpyxl + pymysql)
Scrapy各組件詳細配置:
[Python] 爬蟲 Scrapy框架各組件詳細設置
SQL命令基礎:
[SQL] MySQL基礎+Python交互

轉(zhuǎn)載請注明:陳熹 chenx6542@foxmail.com (簡書號:半為花間酒)
若公眾號內(nèi)轉(zhuǎn)載請聯(lián)系公眾號:早起Python

需求分析

- 主頁面分析

首先點擊http://digimons.net/digimon/chn.html

進入中文檢索頁面

查看頁面源碼

有兩點發(fā)現(xiàn):

  1. 數(shù)據(jù)不是通過Ajax加載
  2. 獲得全部數(shù)據(jù)不需要什么翻頁邏輯素标,所有數(shù)碼獸的后半url都在當前源碼里告材。href中的格式是數(shù)碼獸英文名/index.html

接下來分析幾個數(shù)碼獸詳情頁的url:
http://digimons.net/digimon/agumon_yuki_kizuna/index.html
http://digimons.net/digimon/herakle_kabuterimon/index.html
http://digimons.net/digimon/mugendramon/index.html
http://digimons.net/digimon/king_etemon/index.html

根據(jù)這個情況有一種思路:利用正則或者其他(超)文本解析工具獲取源碼中的所有href挟冠,然后利用urllib.parse.urljoin和父目錄路徑http://digimons.net/digimon/ 拼起來構(gòu)成完整url,再訪問詳情頁

但本文換了一種思路

許多爬蟲的數(shù)據(jù)采集工作都是類似的闻蛀,因此Scrapy提供了若干個 更高程度封裝的通用爬蟲類

可用以下命令查看:

# 查看scrapy提供的通用爬蟲(Generic Spiders)
scrapy genspider -l

CrawlSpider 是通用爬蟲里最常用的一個

通過一套規(guī)則引擎,它自動實現(xiàn)了頁面鏈接的搜索跟進考榨,解決了包含但不限于自動采集詳情頁蛇数、跟進分類/分頁地址等問題。主要運行邏輯是深度優(yōu)先

這個網(wǎng)站的設計非常簡單批销,因此可以考慮用便捷的全網(wǎng)爬取框架洒闸。這個框架的前提是:無關url和需要url有明顯差別,可以利用正則獲取其他方式區(qū)別開

簡而言之,可以想象給定爬蟲一個一只url以后,爬蟲會繼續(xù)訪問從這個url出發(fā)能訪問到的新url捐友,然后爬蟲需要根據(jù)預設的語法判斷這個url是不是所需的蔓纠,如果是則先解析后延伸訪問新url,如果不是則繼續(xù)訪問新url羞反,假如沒有新url該叉結(jié)束

文章中的外鏈比較多是wikipedia布朦,url差別較大。通過對比和數(shù)碼獸詳情頁的url昼窗,可以總結(jié)出所需url的格式:

http://digimons.net/digimon/.*/index.html

利用正則替換中間的英文名

- 詳情頁分析

爬取的需求如下:

基本一只數(shù)碼獸所有的資料都會爬取下來是趴,但需要注意不同的數(shù)碼獸資料不一定完整,故寫代碼需要留意(舉兩個例子)

需求分析差不多了澄惊,可以著手寫代碼

代碼實戰(zhàn)

- 創(chuàng)建項目

# scrapy startproject <Project_name>
scrapy startproject Digimons

# scrapy genspider <spider_name> <domains>
scrapy genspider –t crawl digimons http://digimons.net/digimon/chn.html

- spiders.py

依次打開項目文件夾Digimons - Digimons - spiders唆途,創(chuàng)建digimons.py

# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from ..items import DigimonsItem


class DigimonsSpider(CrawlSpider):
    name = 'digimons'
    allowed_domains = ['digimons.net']
    start_urls = ['http://digimons.net/digimon/chn.html']

    # 爬蟲的規(guī)則是重點富雅,把先前分析的結(jié)果url放進allow
    # callback='parse_item',符合規(guī)則的url回調(diào)該函數(shù)
    # follow = False則爬到該頁面后不繼續(xù)拓寬深度
    rules = (
        Rule(LinkExtractor(allow=r'http://digimons.net/digimon/.*/index.html'), callback='parse_item', follow=False),
    )

    # 按需求逐個解析肛搬,有的不存在需要判斷
    def parse_item(self, response):
        # 名字列表
        name_lst = response.xpath('//*[@id="main"]/article/h2[1]//text()').extract()
        name_lstn = [i.replace('/', '').strip() for i in name_lst if i.strip() != '']
        # 中文名
        Cname = name_lstn[0].replace(' ', '-')
        # 日文名
        Jname = name_lstn[1]
        # 英文名
        Ename = name_lstn[2]
        # 等級
        digit_grade = response.xpath("http://article/div[@class='data'][1]/table/tr[1]/td/text()").extract()
        digit_grade = '-' if digit_grade == [] else ''.join(digit_grade)
        # 類型
        digit_type = response.xpath("http://article/div[@class='data'][1]/table/tr[2]/td/text()").extract()
        digit_type = '-' if digit_type == [] else ''.join(digit_type)
        # 屬性
        digit_attribute = response.xpath("http://article/div[@class='data'][1]/table/tr[3]/td/text()").extract()
        digit_attribute = '-' if digit_attribute == [] else ''.join(digit_attribute)
        # 所屬
        belongs = response.xpath("http://article/div[@class='data'][1]/table/tr[4]/td/text()").extract()
        belongs = '-' if belongs == [] else ''.join(belongs)
        # 適應領域
        adaptation_field = response.xpath("http://article/div[@class='data'][1]/table/tr[5]/td/text()").extract()
        adaptation_field = '-' if adaptation_field == [] else ''.join(adaptation_field)
        # 首次登場
        debut = response.xpath("http://article/div[@class='data'][1]/table/tr[6]/td/text()").extract()
        debut = '-' if debut == [] else ''.join(debut)
        # 名字來源
        name_source = response.xpath("http://article/div[@class='data'][1]/table/tr[7]/td/text()").extract()
        name_source = '-' if name_source == [] else '/'.join(name_source).strip('/')
        # 必殺技
        nirvana = response.xpath("http://article/div[@class='data'][2]/table/tr/td[1]/text()").extract()
        nirvana = '-' if nirvana == [] else '/'.join(nirvana).strip('/')
        # 介紹資料
        info_lst = response.xpath("http://*[@id='cn']/p/text()").extract()
        info = ''.join([i.replace('/', '').strip() for i in info_lst if i.strip() != ''])
        # 圖片url
        img_url = response.xpath('//*[@id="main"]/article/div[1]/a/img/@src').extract()
        img_url = response.url[:-10] + img_url[0] if img_url != [] else '-'
        
        # 個人習慣簡單輸出
        print(Cname, Jname, Ename)

        # 如果要持久化存儲轉(zhuǎn)向items
        item = DigimonsItem()
        item['Cname'] = Cname
        item['Jname'] = Jname
        item['Ename'] = Ename
        item['digit_grade'] = digit_grade
        item['digit_type'] = digit_type
        item['digit_attribute'] = digit_attribute
        item['belongs'] = belongs
        item['adaptation_field'] = adaptation_field
        item['debut'] = debut
        item['name_source'] = name_source
        item['nirvana'] = nirvana
        item['info'] = info
        item['img_url'] = img_url
        yield item

- items.py

關于MySQL存儲的細節(jié)可以參考我的另一篇文章:
[Python] 爬取生信坑論壇 bioinfo帖子相關信息 (requests + openpyxl + pymysql)

# -*- coding: utf-8 -*-

# Define here the models for your scraped items
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.html

import scrapy

# 和spiders.py中的傳入對應
class DigimonsItem(scrapy.Item):
    Cname = scrapy.Field()
    Jname = scrapy.Field()
    Ename = scrapy.Field()
    digit_grade = scrapy.Field()
    digit_type = scrapy.Field()
    digit_attribute = scrapy.Field()
    belongs = scrapy.Field()
    adaptation_field = scrapy.Field()
    debut = scrapy.Field()
    name_source = scrapy.Field()
    nirvana = scrapy.Field()
    info = scrapy.Field()
    img_url = scrapy.Field()

    # 注釋部分是sql語法没佑,需要在命令行運行
    def get_insert_sql_and_data(self):
    # CREATE TABLE digimons(
    # id int not null auto_increment primary key,
    # Chinese_name text, Japanese_name text, English_name text,
    # digit_grade text, digit_type text, digit_attribute text,
    # belongs text, adaptation_field text, debut text, name_source text,
    # narvana text, info text)ENGINE=INNODB DEFAULT CHARSET=UTF8mb4;
        insert_sql = 'insert into digimons(Chinese_name,Japanese_name,English_name,digit_grade,digit_type,' \
                     'digit_attribute,belongs,adaptation_field,debut,name_source,nirvana,info,img_url)' \
                     'values(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)'
        data = (self['Cname'],self['Jname'],self['Ename'],self['digit_grade'],self['digit_type'],
                self['digit_attribute'],self['belongs'],self['adaptation_field'],self['debut'],
                self['name_source'],self['nirvana'],self['info'],self['img_url'])
        return (insert_sql, data)

- pipelines.py

借助items.py和Mysqlhelper完成存儲

# -*- coding: utf-8 -*-
from mysqlhelper import Mysqlhelper
# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html


class DigimonsPipeline(object):
    def __init__(self):
        self.mysqlhelper = Mysqlhelper()

    def process_item(self, item, spider):
        if 'get_insert_sql_and_data' in dir(item):
            (insert_sql, data) = item.get_insert_sql_and_data()
            self.mysqlhelper.execute_sql(insert_sql, data)
        return item

- settings.py

沒有特別修改,下面的代碼默認是注釋狀態(tài)温赔,需要打開

ITEM_PIPELINES = {
   'Digimons.pipelines.DigimonsPipeline': 300,
}

- extends.py

自定義拓展這部分內(nèi)容不看也可以蛤奢,我實現(xiàn)的功能就是在爬蟲運行結(jié)束的時候自動給微信推送消息(借助喵提醒

喵提醒需要申請賬號獲得自己的id,官方已經(jīng)給了一個API可以包裝在本地

from urllib import request, parse
import json

class Message(object):
    def __init__(self,text):
        self.text = text
    def push(self):
        # 重要陶贼,在id中填寫自己綁定的id
        page = request.urlopen("http://miaotixing.com/trigger?" + parse.urlencode({"id": "xxxxxx", "text": self.text, "type": "json"}))
        result = page.read()
        jsonObj = json.loads(result)
        if (jsonObj["code"] == 0):
            print("\nReminder message was sent successfully")
        else:
            print("\nReminder message failed to be sent啤贩,wrong code:" + str(jsonObj["code"]) + ",describe:" + jsonObj["msg"])

寫在另外的py文件里命名為message.py

接下來寫extends.py拜秧,需要自己新建痹屹,位置和pipelines.py,items.py同級

from scrapy import signals
from message import Message


class MyExtension(object):
    def __init__(self, value):
        self.value = value

    @classmethod
    def from_crawler(cls, crawler):
        val = crawler.settings.getint('MMMM')
        ext = cls(val)

        crawler.signals.connect(ext.spider_opened, signal=signals.spider_opened)
        crawler.signals.connect(ext.spider_closed, signal=signals.spider_closed)

        return ext

    def spider_opened(self, spider):
        print('spider running')

    def spider_closed(self, spider):
        # 推送的消息可以自定義腹纳,可以再給出獲取了多少條數(shù)據(jù)
        text = 'DigimonsSpider運行結(jié)束'
        message = Message(text)
        message.push()
        print('spider closed')

重要的是如果添加了自定義拓展痢掠,需要在settings中也打開
默認是:

# Enable or disable extensions
# See https://docs.scrapy.org/en/latest/topics/extensions.html
#EXTENSIONS = {
#    'scrapy.extensions.telnet.TelnetConsole': None,
#}

需要修改并打開:

# Enable or disable extensions
# See https://docs.scrapy.org/en/latest/topics/extensions.html
EXTENSIONS = {
   'Digimons.extends.MyExtension': 500,
}

- running.py

最后就是運行這個項目了,也可以直接在命令行中cd到項目位置后運行

scrapy crawl digimons

個人比較喜歡在py文件中運行嘲恍,新建一個running.py(和items.py同級目錄)

from scrapy.cmdline import execute

execute('scrapy crawl digimons'.split())

最后運行runnings.py這個項目就啟動了

項目運行

運行中如圖:

運行完成共獲得1179條數(shù)據(jù)足画,進入Navicat(MySQL等數(shù)據(jù)庫的圖形交互界面):

數(shù)據(jù)非常好的儲存了,可以看到不是所有的數(shù)碼獸都有所屬陣營

可以做一些簡單的查詢佃牛,比如七大魔王陣營里都有誰:

查詢結(jié)果對比確實只有7只淹辞,重復出現(xiàn)的都是形態(tài)變化或者變體:

對其中重復的一只數(shù)碼獸再次查詢,的確簡介都不同

如果覺得用MySQL查看不方便俘侠,可以在Navicat中轉(zhuǎn)出成EXCEL

在命令行中導出詳情參見:[SQL] MySQL基礎+Python交互


爬取的數(shù)據(jù)EXCEL形式供大家下載:
https://pan.baidu.com/s/1oKFsw3at4cF5p4WpW7ZRcA
提取碼:1wvs

寫在最后
數(shù)碼獸的資料里數(shù)字信息較少象缀,可供挖掘和造模的信息有限
后續(xù)爬取口袋妖怪小精靈們的數(shù)據(jù)就有意思多了 : )

關于數(shù)據(jù)分析的部分以后會涉及,歡迎繼續(xù)關注

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末爷速,一起剝皮案震驚了整個濱河市央星,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌惫东,老刑警劉巖莉给,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異廉沮,居然都是意外死亡颓遏,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進店門滞时,熙熙樓的掌柜王于貴愁眉苦臉地迎上來叁幢,“玉大人,你說我怎么就攤上這事坪稽÷妫” “怎么了鳞骤?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長演训。 經(jīng)常有香客問我弟孟,道長,這世上最難降的妖魔是什么样悟? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任拂募,我火速辦了婚禮,結(jié)果婚禮上窟她,老公的妹妹穿的比我還像新娘陈症。我一直安慰自己,他們只是感情好震糖,可當我...
    茶點故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布录肯。 她就那樣靜靜地躺著,像睡著了一般吊说。 火紅的嫁衣襯著肌膚如雪论咏。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天颁井,我揣著相機與錄音厅贪,去河邊找鬼。 笑死雅宾,一個胖子當著我的面吹牛养涮,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播眉抬,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼贯吓,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蜀变?” 一聲冷哼從身側(cè)響起悄谐,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎库北,沒想到半個月后爬舰,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡贤惯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年洼专,在試婚紗的時候發(fā)現(xiàn)自己被綠了棒掠。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片孵构。...
    茶點故事閱讀 39,932評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖烟很,靈堂內(nèi)的尸體忽然破棺而出颈墅,到底是詐尸還是另有隱情蜡镶,我是刑警寧澤,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布恤筛,位于F島的核電站官还,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏毒坛。R本人自食惡果不足惜望伦,卻給世界環(huán)境...
    茶點故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望煎殷。 院中可真熱鬧屯伞,春花似錦、人聲如沸豪直。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽弓乙。三九已至末融,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間暇韧,已是汗流浹背勾习。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留锨咙,地道東北人语卤。 一個月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像酪刀,于是被迫代替她去往敵國和親粹舵。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,884評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 前言 爬蟲就是請求網(wǎng)站并提取數(shù)據(jù)的自動化程序骂倘,其中請求眼滤,提取,自動化是爬蟲的關鍵历涝。Python作為一款出色的膠水語...
    王奧OX閱讀 3,383評論 1 8
  • Python語言特性 1 Python的函數(shù)參數(shù)傳遞 看兩個如下例子诅需,分析運行結(jié)果: 代碼一: a = 1 def...
    伊森H閱讀 3,065評論 0 15
  • 1 前言 作為一名合格的數(shù)據(jù)分析師,其完整的技術知識體系必須貫穿數(shù)據(jù)獲取荧库、數(shù)據(jù)存儲堰塌、數(shù)據(jù)提取、數(shù)據(jù)分析分衫、數(shù)據(jù)挖掘场刑、...
    whenif閱讀 18,072評論 45 523
  • 寶萊塢的大劇一言不合就載歌載舞,在新上映的《摔跤吧蚪战!爸爸》中牵现,阿米爾汗少了年輕時期的機靈幽默铐懊,畢竟年紀也52,反而...
    黑黑白糖閱讀 336評論 0 2
  • 清晨:5:40透過窗簾縫隙的日光掃過床間瞎疼,隱隱約約聽見樓下有早起老人的談話聲科乎,嘰嘰喳喳的鳥鳴聲。經(jīng)歷了...
    簡單生活enjoylily閱讀 493評論 0 0