最近Python大火,為了跟上時(shí)代,試著自學(xué)了下。Scrapy是一個(gè)高級(jí)的Python爬蟲框架,它不僅包含了爬蟲的特性,還可以方便的將爬蟲數(shù)據(jù)保存到csv、json等文件中。
今天我們就試著用Scrapy來爬取簡書某位作者的全部文章。
在本篇教程中,我們假定您已經(jīng)安裝好Scrapy。 如若不然,請(qǐng)參考 安裝指南 。
1.創(chuàng)建項(xiàng)目
在開始爬取之前,我們必須創(chuàng)建一個(gè)新的Scrapy項(xiàng)目,我這里命名為jianshu_article。打開Mac終端,cd到你打算存儲(chǔ)代碼的目錄中,運(yùn)行下列命令:
//Mac終端運(yùn)行如下命令:
scrapy startproject jianshu_article
2.創(chuàng)建爬蟲程序
//cd到上面創(chuàng)建的文件目錄
cd jianshu_article
//創(chuàng)建爬蟲程序
scrapy genspider jianshu jianshu.com
/*
文件說明:
scrapy.cfg 項(xiàng)目的配置信息,主要為Scrapy命令行工具提供一個(gè)基礎(chǔ)的配置信息。(真正爬蟲相關(guān)的配置信息在settings.py文件中)
items.py 設(shè)置數(shù)據(jù)存儲(chǔ)模型,用于結(jié)構(gòu)化數(shù)據(jù),如:Django的Model
pipelines 數(shù)據(jù)處理行為,如:一般結(jié)構(gòu)化的數(shù)據(jù)持久化
settings.py 配置文件,如:USER_AGENT(模擬瀏覽器,應(yīng)對(duì)網(wǎng)站反爬),遞歸的層數(shù)、并發(fā)數(shù),延遲下載等
spiders 爬蟲目錄,如:創(chuàng)建文件,編寫爬蟲規(guī)則
*/
為了方便編寫程序,我們用Pycharm打開項(xiàng)目,執(zhí)行完上面的命令程序會(huì)自動(dòng)創(chuàng)建目錄及文件,其中生成了一個(gè)jianshu.py的文件,后面我們主要邏輯都將寫在此文件中。
3.設(shè)置數(shù)據(jù)模型
雙擊items.py文件。
找到你想爬取的簡書作者首頁,如我自己的首頁http://www.lxweimin.com/u/6b14223f1b58,用谷歌瀏覽器打開,空白處鼠標(biāo)右擊,單擊“檢查”進(jìn)入控制臺(tái)開發(fā)者模式:
通過分析網(wǎng)頁源碼,我們大概需要這些內(nèi)容:
# -*- coding: utf-8 -*-
# Define here the models for your scraped items
#
# See documentation in:
# https://doc.scrapy.org/en/latest/topics/items.html
import scrapy
class JianshuArticalItem(scrapy.Item):
avatar = scrapy.Field() #頭像
nickname = scrapy.Field() #昵稱
time = scrapy.Field() #發(fā)表時(shí)間
wrap_img = scrapy.Field() #封面(缺省值)
title = scrapy.Field() #標(biāo)題
abstract = scrapy.Field() #正文部分顯示
read = scrapy.Field() #查看人數(shù)
comments = scrapy.Field() #評(píng)論數(shù)
like = scrapy.Field() #喜歡(點(diǎn)贊)
detail = scrapy.Field() #文章詳情url
pass
如此數(shù)據(jù)模型就創(chuàng)建好了,后面運(yùn)行爬蟲的時(shí)候,我得到的數(shù)據(jù)將存進(jìn)模型對(duì)應(yīng)的位置。
4.分析網(wǎng)頁源碼,編寫爬蟲
因?yàn)楸救吮容^懶很少寫文章,文章數(shù)比較少,為了呈現(xiàn)分頁的效果,我在簡書選取了一位作者CC老師_MissCC的主頁進(jìn)行爬取。
我們通過分析URL可以找到一些特征:
作者的URL為:http://www.lxweimin.com/u/ + 作者ID:
文章的URL為:http://www.lxweimin.com/p/ + 文章ID:
雖然我們?cè)跒g覽器直接打開作者的URL,鼠標(biāo)滾輪往下滾動(dòng)會(huì)動(dòng)態(tài)加載下一頁直至最后一篇文章URL還是保持不變。但是作為Scrapy爬蟲貌似只能拿到第一頁,那么如何做到呢?以我個(gè)人多年的開發(fā)經(jīng)驗(yàn)我嘗試在URL后面拼接一個(gè)"page"參數(shù)加上頁數(shù),果不其然,能請(qǐng)求到不同的數(shù)據(jù)。
找到這些規(guī)律,我們就可以通過分析HTML源碼,拿到我們想要的數(shù)據(jù)了。
首先,我們回到j(luò)ianshu.py這個(gè)文件,導(dǎo)入模型:
//從項(xiàng)目名 jianshu_article的文件items.py導(dǎo)入JianshuArticleItem類
from jianshu_article.items import JianshuArticleItem
設(shè)置必要參數(shù)發(fā)起首次請(qǐng)求:
# -*- coding: utf-8 -*-
import scrapy
from jianshu_article.items import JianshuArticleItem
class JianshuSpider(scrapy.Spider):
name = 'jianshu'
allowed_domains = ['jianshu.com']
user_id = "1b4c832fb2ca"
url = "http://www.lxweimin.com/u/{0}?page=1".format(user_id)
start_urls = [
url,
]
def parse(self, response):
#用戶頭像
c = response.xpath('//div[@class="main-top"]/a[@class="avatar"]/img/@src').extract_first()
print(c)
pass
至此終端運(yùn)行命令scrapy crawl jianshu,理論上可以打印網(wǎng)頁內(nèi)容。實(shí)則不然,沒有請(qǐng)求到任何數(shù)據(jù),終端會(huì)打印一些日志信息:
不難發(fā)現(xiàn),報(bào)了403的問題和HTTP status code is not handled or not allowed的問題,導(dǎo)致"Closing spider (finished)"爬蟲終止。通過萬能百度,我知道大概是網(wǎng)站做了一些相應(yīng)的反爬蟲的措施導(dǎo)致的。對(duì)癥下藥,我們只需要在settings.py,做一些相應(yīng)修改就可以了:
```
User_Agent中文名為用戶代理,簡稱 UA,它是一個(gè)特殊字符串頭,使得服務(wù)器能夠識(shí)別客戶使用的
操作系統(tǒng)及版本、CPU 類型、瀏覽器及版本、瀏覽器渲染引擎、瀏覽器語言、瀏覽器插件等。
通俗點(diǎn)講,我們配置這個(gè)字段的目的就是為了偽裝成瀏覽器打開網(wǎng)頁,達(dá)到騙過目標(biāo)網(wǎng)站的監(jiān)測(cè)。
```
USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36'
CONCURRENT_REQUESTS = 1 #并發(fā)數(shù)
DOWNLOAD_DELAY = 5 #為了防止IP被封,我們5秒請(qǐng)求一次
HTTPERROR_ALLOWED_CODES = [403] #上面報(bào)的是403,就把403加入
#默認(rèn)為True,就是要遵守robots.txt 的規(guī)則,這里我們改為False
ROBOTSTXT_OBEY = False
做了相應(yīng)的修改,我們?cè)俅螆?zhí)行爬蟲命令:scrapy crawl jianshu ,看日志打印獲取到頭像。
既然網(wǎng)頁數(shù)據(jù)能爬取成功,我們后面要做的只是分析網(wǎng)頁源碼了,下面就不一一去分析了,體力活。當(dāng)然在此之前你要對(duì)xpath有一定的了解。
下面引用Scrapy中文官網(wǎng)介紹:
從網(wǎng)頁中提取數(shù)據(jù)有很多方法。Scrapy使用了一種基于 XPath 和 CSS 表達(dá)式機(jī)制: Scrapy Selectors。 關(guān)于selector和其他提取機(jī)制的信息請(qǐng)參考 Selector文檔 。
這里給出XPath表達(dá)式的例子及對(duì)應(yīng)的含義:
-
/html/head/title
: 選擇HTML文檔中<head>
標(biāo)簽內(nèi)的<title>
元素 -
/html/head/title/text()
: 選擇上面提到的<title>
元素的文字 -
//td
: 選擇所有的<td>
元素 -
//div[@class="mine"]
: 選擇所有具有class="mine"
屬性的div
元素
上邊僅僅是幾個(gè)簡單的XPath例子,XPath實(shí)際上要比這遠(yuǎn)遠(yuǎn)強(qiáng)大的多。 如果您想了解的更多,我們推薦 這篇XPath教程 。
通過上面的介紹,相信你可以做接下來的爬蟲工作了,下面貼上jianshu.py的全部代碼,以供參考:
# -*- coding: utf-8 -*-
import scrapy
from jianshu_article.items import JianshuArticleItem
class JianshuSpider(scrapy.Spider):
name = 'jianshu'
allowed_domains = ['jianshu.com']
user_id = "1b4c832fb2ca" #替換此用戶ID可獲取你需要的數(shù)據(jù),或者放開下一行的注釋
#user_id = input('請(qǐng)輸入作者id:\n')
url = "http://www.lxweimin.com/u/{0}?page=1".format(user_id)
start_urls = [
url,
]
def parse(self, response):
# [關(guān)注,粉絲,文章]
a = response.xpath('//div[@class="main-top"]/div[@class="info"]/ul/li/div/a/p/text()').extract()
print(a)
# [字?jǐn)?shù),收獲喜歡]
b = response.xpath('//div[@class="main-top"]/div[@class="info"]/ul/li/div/p/text()').extract()
print(b)
# 大頭像
c = response.xpath('//div[@class="main-top"]/a[@class="avatar"]/img/@src').extract_first()
print(c)
# 用戶名
d = response.xpath('//div[@class="main-top"]/div[@class="title"]/a/text()').extract_first()
print(d)
# 性別
e = response.xpath('//div[@class="main-top"]/div[@class="title"]/i/@class').extract_first()
print(e)
# 獲取文章總數(shù),計(jì)算頁數(shù)。(簡書網(wǎng)站默認(rèn)每頁是9組數(shù)據(jù))
temp = int(a[2])
if (temp % 9 > 0):
count = temp // 9 + 1
else:
count = temp // 9
print("總共" + str(count) + "頁")
base_url = "http://www.lxweimin.com/u/{0}?page={1}"
for i in range(1, count + 1):
i = count + 1 - i #理論上正序1~count就是按順序獲取的,但是獲取的數(shù)據(jù)是倒置的,所以我們獲取count~1的數(shù)據(jù),得到的數(shù)組就是按照網(wǎng)頁形式1~count頁碼排序的了
yield scrapy.Request(base_url.format(self.user_id, i), dont_filter=True, callback=self.parse_page)
#迭代返回每頁的內(nèi)容
def parse_page(self, response):
for sel in response.xpath('//div[@id="list-container"]/ul/li'):
item = JianshuArticleItem()
item['wrap_img'] = sel.xpath('a/img/@src').extract_first()
item['avatar'] = sel.xpath('div//a[@class="avatar"]/img/@src').extract_first()
item['nickname'] = sel.xpath('div//a[@class="nickname"]/text()').extract_first()
item['time'] = sel.xpath('div//span[@class="time"]/@data-shared-at').extract_first()
item['title'] = sel.xpath('div/a[@class="title"]/text()').extract_first()
item['abstract'] = sel.xpath('div/p[@class="abstract"]/text()').extract_first()
item['read'] = sel.xpath('div/div[@class="meta"]/a[1]/text()').extract()[1]
item['comments'] = sel.xpath('div/div[@class="meta"]/a[2]/text()').extract()[1]
item['like'] = sel.xpath('div/div[@class="meta"]/span/text()').extract_first()
item['detail'] = sel.xpath('div/a[@class="title"]/@href').extract_first()
yield item
至此爬蟲代碼編寫完畢,如果要把獲取的數(shù)據(jù)保存下來,你可以終端執(zhí)行如下命令:
/*
此命令用于把爬取的數(shù)據(jù)保存為json文件格式,當(dāng)然你也可以保存為別的文件格式。
Scrapy官方列出的文件格式有如下幾種:('json', 'jsonlines', 'jl', 'csv', 'xml', 'marshal', 'pickle')。
溫馨提示:如果要再次爬取,最好換一個(gè)文件名或者清空數(shù)據(jù)再爬取,因?yàn)榈诙€是寫入上一個(gè)文件,數(shù)據(jù)不會(huì)覆蓋,
會(huì)堆積在上次獲取的下面,造成json文件格式報(bào)錯(cuò)。
*/
scrapy crawl jianshu -o data.json
程序執(zhí)行完后,我們可以在文件目錄看到新生成的data.json文件,雙擊可以看到我們要獲取的全部數(shù)據(jù):
github地址:https://github.com/leesonp/jianshu_article
至此以上就是本文的全部內(nèi)容,謝謝閱讀。