概述
在上一篇文章《爬蟲學(xué)習(xí)之一個(gè)簡單的網(wǎng)絡(luò)爬蟲》中我們對(duì)爬蟲的概念有了一個(gè)初步的認(rèn)識(shí)溃卡,并且通過Python的一些第三方庫很方便的提取了我們想要的內(nèi)容堕阔,但是通常面對(duì)工作當(dāng)作復(fù)雜的需求,如果都按照那樣的方式來處理效率非常的低鲜结,這通常需要你自己去定義并實(shí)現(xiàn)很多非常基礎(chǔ)的爬蟲框架上的功能,或者需要組合很多Python第三方庫來做崎苗。不過不用擔(dān)心,Python中有很多非常優(yōu)秀的爬蟲框架舀寓,比如我們接下來要學(xué)習(xí)到的Scrapy胆数。Scrapy官方有很經(jīng)典的入門文檔說明,這一篇僅僅是通過一個(gè)簡單的實(shí)例來了解Scrapy這個(gè)庫是如何來進(jìn)行網(wǎng)絡(luò)內(nèi)容提取的互墓,更深入的學(xué)習(xí)請(qǐng)閱讀Scrapy官方文檔必尼。
建立目標(biāo)
同樣在做任何事情之前都需要明確目標(biāo),那這次我們的目標(biāo)是爬取一些技術(shù)性的文章并存儲(chǔ)到數(shù)據(jù)庫中。這就需要有目標(biāo)網(wǎng)址和數(shù)據(jù)庫結(jié)構(gòu)判莉,數(shù)據(jù)庫我們選擇使用MySql豆挽,目標(biāo)網(wǎng)站我們找了一個(gè)叫腳本之家的內(nèi)容站。我們這里首先準(zhǔn)備好一張用于存儲(chǔ)文章的表結(jié)構(gòu):
CREATE TABLE `articles` (
`id` mediumint(8) AUTO_INCREMENT NOT NULL,
`title` varchar(255) DEFAULT NULL,
`content` longtext,
`add_date` int(11) DEFAULT 0,
`hits` int(11) DEFAULT '0',
`origin` varchar(500) DEFAULT '',
`tags` varchar(45) DEFAULT '',
PRIMARY KEY (`id`),
KEY `add_date` (`add_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
分析目標(biāo)結(jié)構(gòu)
這里我們首先需要爬取得入口是“網(wǎng)絡(luò)編程”這個(gè)節(jié)點(diǎn)券盅,主入口網(wǎng)址為(http://www.jb51.net/list/index_1.htm) 打開這個(gè)網(wǎng)站我們通過Chrome或者其他瀏覽器的查看元素來分析當(dāng)前頁面的HTML語義結(jié)構(gòu)帮哈,如下圖所示:
從圖中紅色框線的部分可以看出,這里是我們需要在“網(wǎng)絡(luò)編程”這個(gè)節(jié)點(diǎn)下需要提取的所有文章的主分類入口锰镀,通過這些入口可以進(jìn)去到不同文章分類的列表中娘侍。所以根據(jù)初步結(jié)構(gòu)分析,我們得出本次爬蟲的爬取路線為:
從主入口進(jìn)去 -> 提取當(dāng)前入口中的所有分類 -> 通過分類入口進(jìn)入到分類列表 -> 通過列表進(jìn)入到文章頁
分類入口確定了接下來看看我們的分類列表泳炉,隨意點(diǎn)開一個(gè)分類入口私蕾,打開列表如下圖所示:
這里我框出了兩個(gè)主要部分,第一個(gè)是文章的標(biāo)題胡桃,第二個(gè)是分頁踩叭,文章對(duì)應(yīng)的URL就是我們接下來需要爬取文章內(nèi)容的入口,這里需要注意的是分頁的處理翠胰,通過分頁的最后一頁我們可以知道當(dāng)前這類列表共有多少頁文章容贝。結(jié)合以上分析我們基本確定了本次爬蟲的各個(gè)路線入口,接下來我們就開始通過程序來實(shí)現(xiàn)本次的目標(biāo)之景。
實(shí)現(xiàn)爬蟲
在實(shí)現(xiàn)爬蟲之前我們通過一張圖來對(duì)Scrapy有個(gè)基本的認(rèn)識(shí)斤富,為了保持本章內(nèi)容的簡潔性,我們這里暫時(shí)不會(huì)討論Item Pipeline部分锻狗,Scrapy架構(gòu)圖如下所示(圖片來自網(wǎng)絡(luò)):
從圖中可以很清晰的看到Scrapy所包含的幾大塊衰伯,下面我們通過代碼來演示我們所用到的基礎(chǔ)功能部分吵瞻。
主要依賴第三方庫:
web.py web框架霞势,這里只用到了database部分催植,將來會(huì)用來進(jìn)行內(nèi)容展示
scrapy 爬蟲框架,這里只用到了最基本的內(nèi)容提取
這里還會(huì)用到一些xpath相關(guān)知識(shí)刻帚,請(qǐng)自行Google了解xpath語法
# -*- coding:utf-8 -*-
'''by sudo rm -rf http://imchenkun.com'''
import scrapy
from scrapy.http import Request
import web
import time
db = web.database(dbn='mysql', host='127.0.0.1', db='imchenkun', user='root', pw='root')
# 允許的站點(diǎn)域
allow_domain = "jb51.net"
base_url = "http://www.jb51.net"
# 列表頁
list_url = "http://www.jb51.net/list/list_%d_%d.htm"
# 列表分頁
list_page = 1
# 文章頁
crawl_url = "http://www.jb51.net/article/%d.htm"
class JB51Spider(scrapy.Spider):
name = "jb51"
start_urls = [
"http://www.jb51.net/list/index_1.htm"
]
cate_list = []
def parse(self, response):
cate_id = response.selector.xpath('//div[@class="index_bor clearfix"]/div[@class="index_con"]/span/a/@href').re('(\\\\d+)')[::2]
for id in cate_id:
cate_url = list_url % (int(id), 1)
yield Request(cate_url, callback=self.parse_page)
def parse_page(self, response):
_params = response.selector.xpath('//div[@class="dxypage clearfix"]/a[last()]/@href').re('(\\\\d+)')
cate_id = int(_params[0]) # 分類編號(hào)
count = int(_params[1]) # 總頁數(shù)
article_urls = response.selector.xpath('//div[@class="artlist clearfix"]/dl/dt/a/@href').extract()
# 處理第一頁
for article_url in article_urls:
yield Request(base_url + article_url, callback=self.parse_article)
# 處理其他頁
for page in range(1, count):
url = (list_url % (cate_id, page + 1))
yield Request(url, callback=self.parse_list)
def parse_list(self, response):
"""解析文章列表"""
article_urls = response.selector.xpath('//div[@class="artlist clearfix"]/dl/dt/a/@href').extract()
for article_url in article_urls:
yield Request(base_url + article_url, callback=self.parse_article)
def parse_article(self, response):
"""解析文章內(nèi)容"""
title = response.selector.xpath('//div[@class="title"]/h1/text()').extract()[0]
content = response.selector.xpath('//div[@id="content"]').extract()[0]
tags = ','.join(response.selector.xpath('//div[@class="tags mt10"]/a/text()').extract())
results = db.query('select count(0) as total from articles where origin=$origin', vars = { 'origin': response.url })
if results[0].total <= 0:
db.insert('articles',
title=title,
origin=response.url,
content=content,
add_date=int(time.time()),
hits=0,
tags=tags
)
安裝Scrapy后以上代碼通過以下命令執(zhí)行:
scrapy runspider jb51_spider.py
本次運(yùn)行后的效果在數(shù)據(jù)庫中可以見如下圖所示:
總結(jié)
本篇文章我們主要了解了基本的Scrapy Spider部分潦嘶,而且通過對(duì)目標(biāo)網(wǎng)站的結(jié)構(gòu)分析使用xpath進(jìn)行內(nèi)容的提取,以及分頁的處理崇众。這里我們的目的是建立一種寫爬蟲的思路掂僵,而不在于怎么使用工具來爬數(shù)據(jù)。首先確定目標(biāo)顷歌,然后分析目標(biāo)锰蓬,再借助現(xiàn)有工具進(jìn)行內(nèi)容提取,提取內(nèi)容的過程中會(huì)遇到各種問題眯漩,這個(gè)時(shí)候我們?cè)賮碇饌€(gè)解決這些問題芹扭,直到我們的爬蟲能夠無障礙的運(yùn)行。接下來我會(huì)使用Scrapy更多的功能將繼續(xù)探索Item的定義,Pipeline的實(shí)現(xiàn)以及如何使用代理冯勉。
特別申明:本文所提到的腳本之家網(wǎng)站只是拿來進(jìn)行爬蟲的技術(shù)交流學(xué)習(xí),讀者涉及到的所有侵權(quán)問題都與本人無關(guān)摹芙,也希望大家在學(xué)習(xí)實(shí)戰(zhàn)的過程中不要大量的爬取內(nèi)容對(duì)服務(wù)器造成負(fù)擔(dān)
本文首發(fā)在sudo rm -rf 轉(zhuǎn)載請(qǐng)注明原作者