快速入門Scrapy--打賞用什么措辭最吸金?
Scrapy是一款網絡爬蟲框架,官方文檔的描述如下:
Scrapy是一個為了爬取網站數據,提取結構性數據而編寫的應用框架。 可以應用在包括數據挖掘,信息處理或存儲歷史數據等一系列的程序中。
其最初是為了 頁面抓取 (更確切來說, 網絡抓取 )所設計的, 也可以應用在獲取API所返回的數據(例如 Amazon Associates Web Services ) 或者通用的網絡爬蟲。
以前寫小型爬蟲的話還可以自己寫,用urllib,BeautifulSoup,Requests什么的就能解決了,后來我發現遇到一個新問題又得重新來一遍這些代碼,又得去看前面是怎么寫的,而且自己寫容易怎么高興怎么來,代碼寫的太亂,不好維護,過段時間再來看又要花時間才能看懂。
用框架的好處就是代碼結構清晰,代碼重用,不用對新的問題又重新來一遍代碼,而且功能更強大,能快速解決自己手寫代碼所不能短時間解決的問題。
平臺
- Windows 8.1
- Python 2.7.10
- 簡書
Scrapy安裝
Scrapy完美支持Python 2.x,雖然現在已經慢慢在支持Python 3.x了,但是可能還會遇到不少情況。我剛開始學習Scrapy想用Python 3.5的,都安裝好了,但是運行的時候還是有引包錯誤:
ImportError: cannot import module '_win32stdio'
搜了一些,也沒有解決,而且后面可能還會有很多問題,就暫時等一等它們的更新吧,先用回2.7,解決問題再說。
(By the way,看到了下面這個)
在Windows,Python 3.x下不能簡單的 pip install scrapy 來一條龍安裝scrapy,因為中間會出一些錯誤。
我參考了 【1】 以及 【2】 ,采用安裝wheel文件的方式極其有效。
Python 3.5下Scrapy安裝步驟:
1.安裝Python,這個不說了
2.去 http://www.lfd.uci.edu/~gohlke/pythonlibs/#lxml 下載合適你的Python版本的lxml的wheel文件,我下載的是 lxml-3.4.4-cp35-none-win32.whl ,下載3.6.0版本好像不得行,在我的平臺上報錯:
lxml-3.6.0-cp35-cp35m-win32.whl is not a supported wheel on this platform ,不支持我的平臺。
下載完后,將whl文件拷貝到Python安裝目錄下,然后cmd進入到你的Python安裝目錄,運行
pip3 install lxml-3.4.4-cp35-none-win32.whl
然后運行:
pip3 install scrapy
在cmd中輸入scrapy,如果輸出版本信息并沒有報錯,那么恭喜你,搞定了,是不是很爽!
Python 2.7下Scrapy的安裝
Python2.7下直接 pip install scrapy ,如果報錯,看報錯的內容是什么,找出問題出在哪個依賴包上,在網上搜索該包的whl文件(符合版本),直接pip install whl文件 來安裝就好了。我是問題處在twisted包上,所以去網上下載了老版本的twisted安裝的。
開始項目
項目目的
我們將所學的馬上利用到實際問題中來。[5]
爬取簡書首頁文章的打賞描述和打賞數,以企獲得打賞描述對打賞數的影響
其實打賞數這個東西和文章的質量是最相關的,但是通過大量數據的挖掘統計,是否能將這種相關性弱化一下,從而顯露出打賞描述和打賞數的關系呢?這就有趣了,值得研究。而且還可以同時學習框架和做有趣的事,豈不是人生一大樂趣。
創建Scrapy項目
通過如下語句創建Scrapy項目:
scrapy startproject jianshu2
然后會生成一個目錄jianshu,目錄結構如下:
jianshu/
scrapy.cfg
jianshu/
__init__.py
items.py
pipelines.py
settings.py
spiders/
__init__.py
- spiders目錄存放主爬取代碼,是整個項目的核心。需要在spider下自己新建自己的爬取程序。
- scrapy.cfg是項目的配置文件。
- settings.py是項目的設置文件。
- items.py定義我們要爬取的字段信息。
- pipelines.py是項目的管道文件。
定義items.py
首先定義我們需要爬取的字段:
# -*- coding: utf-8 -*-
import scrapy
class Jianshu2Item(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
url = scrapy.Field()
likeNum = scrapy.Field()
編寫真正的Spider主爬程序
import scrapy
class postSpider(scrapy.spiders.Spider):
name = 'post'
start_urls = ['http://www.jianshu.com']
def parse(self, response):
articles = response.xpath('//ul[@class="article-list thumbnails"]/li')
for article in articles:
url = article.xpath('div/h4/a/@href').extract()
likeNum = article.xpath('div/div/span[2]/text()').extract()
print(url,likeNum)
然后試著運行:
scrapy crawl post
來運行我們的爬蟲,中間又報了一次”No module named win32api”錯誤,直接pip install pypiwin32即可。
然后可以看到正確運行了,爬取了20篇文章后,爬蟲自動停止,cmd中打印正常。
中間用到了XPath來解析HTML,找到元素具體的位置,我們找到首頁的HTML的第一篇文章:
articles = response.xpath('//ul[@class="article-list thumbnails"]/li')
這句找到所有文章的HTML段,response是我們爬取時服務器返回的HTML。
我們看到所有文章都包含在 <ul class="article-list thumbnails"> 中,并且以 <li class=have-img> 開頭,所以就不難理解XPath中為什么這么寫了。
有BeautifulSoup基礎的同學應該很好理解XPath了。
使用Item
我們爬取數據肯定不是為了打印出來看一下就算了,而是想要保存數據,一般來說,Spider爬取到數據之后通過items返回,還記得我們之前定義的items么,這時候就可以派上用場了。
寫出完整代碼:
import scrapy
from jianshu2.items import Jianshu2Item
class postSpider(scrapy.spiders.Spider):
name = 'post'
start_urls = ['http://www.jianshu.com']
def parse(self, response):
articles = response.xpath('//ul[@class="article-list thumbnails"]/li')
for article in articles:
url = article.xpath('div/h4/a/@href').extract()
likeNum = article.xpath('div/div/span[2]/text()').extract()
item = Jianshu2Item()
item['url'] = 'http://www.jianshu.com/'+url[0]
if likeNum == []:
#print(url,likeNum)
item['likeNum'] = 0
else:
#print(url,int(likeNum[0].split(' ')[-1]))
item['likeNum'] = int(likeNum[0].split(' ')[-1])
yield item
執行 scrapy crawl post -o items.json 就把數據保存到json中了。
yield 語句提交item。
注意打賞有可能沒有,所以span也沒有,這里判斷一下。
數據如下:
[
[
{"url": "http://www.jianshu.com//p/6d7bf7d611aa", "likeNum": 1},
{"url": "http://www.jianshu.com//p/e47d86ce78d4", "likeNum": 0},
{"url": "http://www.jianshu.com//p/e69606806d6c", "likeNum": 0},
{"url": "http://www.jianshu.com//p/d7159874c59c", "likeNum": 2},
{"url": "http://www.jianshu.com//p/d38e8074ae94", "likeNum": 0},
{"url": "http://www.jianshu.com//p/6c8a0d0447cd", "likeNum": 0},
{"url": "http://www.jianshu.com//p/beff4ff80b25", "likeNum": 0},
{"url": "http://www.jianshu.com//p/d7e626cf02d7", "likeNum": 0},
{"url": "http://www.jianshu.com//p/524b13db9ce3", "likeNum": 1},
{"url": "http://www.jianshu.com//p/39449bcf9c28", "likeNum": 0},
{"url": "http://www.jianshu.com//p/970412b3c34d", "likeNum": 0},
{"url": "http://www.jianshu.com//p/2f98170f6eda", "likeNum": 1},
{"url": "http://www.jianshu.com//p/e91ab8e7a517", "likeNum": 0},
{"url": "http://www.jianshu.com//p/59a6caf3d965", "likeNum": 1},
{"url": "http://www.jianshu.com//p/ee5432e57dd3", "likeNum": 0},
{"url": "http://www.jianshu.com//p/00b7662bd335", "likeNum": 0},
{"url": "http://www.jianshu.com//p/1815b4071362", "likeNum": 1},
{"url": "http://www.jianshu.com//p/b00f7a2f0295", "likeNum": 0},
{"url": "http://www.jianshu.com//p/7f5fc5a01b75", "likeNum": 0},
{"url": "http://www.jianshu.com//p/84c10f2cf100", "likeNum": 0}
]
我們想將數據直接存在CSV這樣的文件中怎么辦呢?方法就是使用 Feed exports ,在settings.py文件中添加:
FEED_URI=u'D:\Python27\jianshu2\jianshu2\spiders\data.csv'
FEED_FORMAT='CSV'
第一次運行 scrapy crawl post -o data.csv ,然后后面不用加-o data.csv,即可輸出到data.csv中。
獲取求打賞聲明
我們已經獲得了url和打賞數,這已經是一個巨大的進步了。
然而我們還需要根據這個url再進一步爬到文章里面去,并且我們希望在一個爬蟲里面就解決了,不想搞很多爬蟲。
這時候問題轉化為: 如何爬取需要的屬性在不同頁面的items?
這時候我們加一個屬性’quote’,這個屬性在打開url的頁面中。
這時候,看到 這里 ,仿照它的寫法,通過meta傳遞item參數,即相當于
主函數先確定一些參數(‘url’,’likeNum’),剩下的交給另一個函數去做,然后另一個函數算出’quote’參數后把item還給主函數,主函數整合一下item,然后yield生成就好了。
部分代碼:
request = Request(posturl,callback=self.parse_donate)
request.meta['item'] = item
yield request
...
def parse_donate(self, response):
donate = response.xpath('//div[@class="support-author"]/p/text()').extract()
item = response.meta['item']
if len(str(donate)) == 0:
item['quote'] = ""
else:
item['quote'] = str(donate[0].encode('utf-8'))
return item
爬取多頁
這時候我們發現爬的太少了,只有20篇。又看到首頁下面有一個【點擊查看更多】按鈕,我們試著在代碼中‘按下’這個按鈕,然后獲取下面內容的url,遞歸調用parse即可添加更多的文章。
next_link = selector.xpath('//*[@id="list-container"]/div[@class="load-more"]/button/@data-url').extract()[0]
if next_link:
next_link = self.url + str(next_link)
yield Request(next_link,callback=self.parse)
通過Pipeline寫入json文件
有了item之后,item會被傳遞給Item Pipeline,我們可以在pipelines.py中對item做一些操作。
Item Pipeline的典型應用如下,更多見中文文檔。
- 清洗HTML數據
- 驗證item中的數據
- 查重或者丟棄
- 保存結果到文件(json,數據庫,csv等)
于是我們編寫pipelines.py如下:
import json
import codecs
class Jianshu2Pipeline(object):
def __init__(self):
self.file = codecs.open('items.json','wb','utf-8')
def process_item(self, item, spider):
line = json.dumps(dict(item)) + "\n"
self.file.write(line.decode("unicode_escape"))
return item
不得不用codecs來解決編碼問題。Python在Windows下的編碼真讓人頭疼。
這時候我們寫道json中,其實url都可以去掉了,我們并不關心。
效果如下:
合并打賞描述,根據打賞數排序
修改pipelines.py文件,用一個全局的字典dict記錄每種語句的打賞數之和,然后根據打賞數排序,寫到新的csv文件中。
# -*- coding: utf-8 -*-
import json
import codecs
from operator import itemgetter
class Jianshu2Pipeline(object):
def __init__(self):
self.file = codecs.open('items.json','wb','utf-8')
self.quote = {}
self.filecsv = codecs.open('items.csv','w','utf-8')
def process_item(self, item, spider):
line = json.dumps(dict(item)) + "\n"
self.file.write(line.decode("unicode_escape"))
if item['quote'] in self.quote.keys():
self.quote[item['quote']] += item['likeNum']
else:
self.quote[item['quote']] = item['likeNum']
self.filecsv.seek(0)
lis = sorted(self.quote.items(),key=itemgetter(1),reverse=True)
for i in range(len(lis)):
line2 = lis[i][0] + '\t' + str(lis[i][1]) + '\r\n'
self.filecsv.write(line2.decode("utf-8"))
return item
結語
由結果可以看出,第一條打賞數最多,不難理解,因為這句是默認的打賞描述,所以使用的基數很大,所以不能說明什么。由于數據量太少,只能爬6頁,所以還不是很能說明問題。但是學習scrapy,了解scrapy的目的已經初步達到了,雖然還只是初步學習。但是找出統計上相對能夠吸引人打賞的描述的目的還沒有達到,需要加大數據量。
由結果還可以看出,其實打賞描述的個性化挺強的,很多都是個人信息。所以呢,還是要大數據。
查看源碼點擊進入我的Github: 本文源碼
來自: http://whatbeg.com/2016/05/19/learnscrapy.html