受大牛們的鼓勵,繼2個熬夜的晚上終于做完scrapy版的“七日熱點”。寶寶好累,做到凌晨,電腦差一點被收走。 趕緊開始記錄。
主要參考文章:
Scrapy 0.24 文檔
Scrapy爬取"單頁面"數(shù)據(jù)(一)
Scrapy爬取多層網(wǎng)頁結(jié)構(gòu)數(shù)據(jù)(二)
Scrapy抓取多層網(wǎng)頁結(jié)構(gòu)詳解(三)
爬蟲小分隊二組Scrapy框架收錄專題-20170423(二)
Scrapy 是專門用來爬取網(wǎng)站數(shù)據(jù)的應用框架。不能直接用pycharm來創(chuàng)建一個scrapy的project。需要現(xiàn)在shell中寫入scrapy startproject 項目名稱。然后打開pycharm,就會生成類似于如下多個py文件組成項目。
項目名稱/
scrapy.cfg
tutorial/
__init__.py
items.py
pipelines.py
settings.py
spiders/
__init__.py
...
具體每個py文件的功能可以查找相關(guān)資料?,F(xiàn)在需要做的是在spiders目錄下新建一個你的爬蟲主的py文件。這就是你要寫爬蟲的主要地方。
代碼如下組成
1、items.py
存儲爬取數(shù)據(jù)。 其類型類似于詞典。用于聲明可用字段的簡單語法。
from scrapy import Item,Field
class SevendayItem(Item):
# define the fields for your item here like:
# name = scrapy.Field()
user=Field() #作者
title=Field() #標題
read_qty=Field() #閱讀量
commend_qty=Field() #評論數(shù)量
admire_qty=Field() #喜歡數(shù)量
reward=Field() #打賞
topic=Field() #被收入專題
2、settings.py
設(shè)置CSV文件存儲位置和本機的user_agent
USER_AGENT='Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36'
FEED_URI=u'sevenday.csv'
FEED_FORMAT='CSV'
3、main.py
啟動爬蟲
#-*- coding:utf-8 -*-
from scrapy import cmdline
cmdline.execute("scrapy crawl sevenday1".split())
4、sevenday.py
#-*- coding:utf-8 -*-
import sys
sys.path.append('..')
from scrapy.spiders import CrawlSpider
from scrapy.http import Request
from items import SevendayItem
from scrapy.selector import Selector
import sys
import re
import json
reload(sys)
sys.setdefaultencoding('utf-8')
class sevenday(CrawlSpider):
name='sevenday1'
start_urls=['http://www.itdecent.cn/trending/weekly?utm_medium=index-banner-s&utm_source=desktop&page=1']
def parse(self, response):
selector=Selector(response)
datas=selector.xpath('//ul[@class="note-list"]/li')
for data in datas:
base_url = data.xpath('div/a/@href')
if base_url:
detail_url='http://www.itdecent.cn'+data.xpath('div/a/@href').extract()[0]
print 'ddd',detail_url
yield Request(detail_url,callback=self.parse_item)
urls = ['http://www.itdecent.cn/trending/weekly?utm_medium=index-banner-s&utm_source=desktop&page={}'.format(i) for i in range(2, 9)]
for newurl in urls:
yield Request(newurl, callback=self.parse)
def parse_item(self, response):
#items=[]
item=SevendayItem()
selector=Selector(response)
user=selector.xpath('//span[@class="name"]/a/text()').extract()[0]
title=selector.xpath('//div[@class="article"]/h1[@class="title"]/text()').extract()[0]
read_qty=re.findall('"views_count":(.*?),', response.text, re.S)[0]
comment_qty = re.findall('"comments_count":(.*?),', response.text, re.S)[0]
admire_qty=re.findall('"likes_count":(.*?),', response.text, re.S)[0]
id = re.findall('"id":(.*?),', response.text, re.S)[0]
reward_url = ['http://www.itdecent.cn/notes/{}/rewards.json'.format(str(id))]
print reward_url[0]
item['user']=user
item['title']=title
item['read_qty']=read_qty
item['commend_qty']=comment_qty
item['admire_qty']=admire_qty
#items.append(item)
#yield Request(reward_url[0],meta={'id':id,'user':user,'title':title,'read_qty':read_qty,'comment_qty':comment_qty,'admire_qty':admire_qty},callback=self.parse_info)
yield Request(reward_url[0],meta={'id':id,'item':item},callback=self.parse_info)
def parse_info(self,response):
selector=Selector(response)
item1=response.meta['item']
item=SevendayItem()
item['user']=item1['user']
item['title'] = item1['title']
item['read_qty'] = item1['read_qty']
item['commend_qty'] = item1['commend_qty']
item['admire_qty'] = item1['admire_qty']
reward_detail = json.loads(response.text)
reward_qty = reward_detail['rewards_count']
print 'ssss',reward_qty
item['reward']=reward_qty
id=response.meta['id']
html_collection_url = 'http://www.itdecent.cn/notes/%s/included_collections.jason' % id
yield Request(html_collection_url,meta={'item':item},callback=self.parse_collection)
def parse_collection(self,response):
item1=response.meta['item']
item=SevendayItem()
datas=[]
collection_detail=json.loads(response.text)
for one in collection_detail['collections']:
datas.append(one['title'])
data = ','.join(datas)
item['topic']=data
item['user'] = item1['user']
item['title'] = item1['title']
item['read_qty'] = item1['read_qty']
item['commend_qty'] = item1['commend_qty']
item['admire_qty'] = item1['admire_qty']
item['reward']=item1['reward']
yield item
重點分析sevenday.py部分知識點:
class sevenday(CrawlSpider):
name='sevenday1' #爬蟲名字,
start_urls=['http://www.itdecent.cn/trending/weekly?utm_medium=index-banner-s&utm_source=desktop&page=1']
def parse(self, response):
urls = ['http://www.itdecent.cn/trending/weekly?utm_medium=index-banner-s&utm_source=desktop&page={}'.format(i) for i in range(2, 9)]
for newurl in urls:
yield Request(newurl, callback=self.parse)
name='sevenday1'是爬蟲名字,要和上邊的類“sevenday"取不一樣的名稱,在main.py 會啟動這個爬蟲名稱來運行爬蟲
start_urls是啟動爬蟲最初進入的url
def parse(self, response):是一個用來解析這個start_urls的頁面的函數(shù)。
其中response就是請求返回的響應的參數(shù)。
它的作用就像相當于單線程爬蟲中的:
url='http://*****'
html=requests.get(url).content
可以用parse函數(shù)來解析start_urls的HTML源碼來提取數(shù)據(jù)。例如:
def parse(self, response):
selector=Selector(response)
datas=selector.xpath('//ul[@class="note-list"]/li')
類Selector是一個Scrapy的選擇器。通過特定的XPath表達式來“選擇” HTML文件中的某個部分。上邊的例子通過xpath選取包含class="note-list"的標簽ul下的li標簽。通過網(wǎng)頁分析,datas是一個列表。不確定的可以在這里打印datas看一下。
接上邊,urls是一個新創(chuàng)建的url列表,通過for循環(huán),游遍所有urls的地址.
yield Request(newurl, callback=self.parse) yield是提交的意思。callback是一個回調(diào)函數(shù)。本句的意思是將新的newurl重新回調(diào)給parse函數(shù)自身。然后parse函數(shù)收到新的newurl函數(shù),解析newurl的網(wǎng)頁代碼。
另外,Request還可以在請求下一頁解析的時候,將本頁的數(shù)據(jù)通過meta傳遞給下一頁。meta是一個字典。里邊通過key對應value來傳遞給下一個回調(diào)對象。
方法如下:
yield Request(newurl, meta={'key1':'value1','key2':'value2'},callback=self.parse_info)
def parse_info(self,response) #這里已經(jīng)解析了newurl的網(wǎng)頁代碼,可以獲取新的網(wǎng)頁數(shù)據(jù)
item1=SevendayItem() #假定items.py的item名稱是SevendayItem,將它實例化給item1,注意item1是一個字典。
item1['key1']=response.meta['key1'] #response中meta中的key1的value賦予item['key1']
item1['key2']=response.meta['key2'] #response中meta中的key2的value賦予item['key2']
.....
selector=Selector(response) ##這里已經(jīng)解析了newurl的網(wǎng)頁代碼
value=selector.xpath('//../..').extract()[0] #通過xpath獲取newurl網(wǎng)頁的相對應的數(shù)據(jù),注意獲取的數(shù)據(jù)需要在后邊加上`.extract`,要不提取不出來
item1['key3']=value #value賦予 item1['key3']
yield item1 提交item1
yield item1提交最終的數(shù)據(jù)給items.py,如果還有數(shù)據(jù)在下一級網(wǎng)頁,可以創(chuàng)建新的url,然后將item1的值賦予meta,繼續(xù)請求新的url回調(diào)下一個parse_**
yield Request(新的url,meta={},callback=self.parse_**)
def parse_**(self,response)
“7日熱點”遇到BUG的解決處理
由于打賞數(shù)據(jù)是異步加載,需要在界面中用正則表達式提取id,然后重新構(gòu)造新的reward_rul,由于是json網(wǎng)頁,所以需要用方法json.load解析網(wǎng)頁,取出我們要rewards_count


以上2個圖可以看到reward_url是在Request URL中,加載后的數(shù)據(jù)是在下圖中的rewards_count:8
我剛開始做的時候和單線程爬蟲一樣,構(gòu)造如下的reward_url:
rewards_url = ['http://www.itdecent.cn/notes/{}/rewards.count=20'.format(str(id))][0]然后將它提交,回調(diào)給下一個parse_info函數(shù)。
我在解析parse_info的時候,提取不出reward_count,生成的CSV文件是空的。于是打斷點進行調(diào)試:運行debug時候,系統(tǒng)提示如下:

中間有個DEBUG Forbidden by robots.txt ,然后去網(wǎng)上查找資料,有人說關(guān)閉scrapy自帶的ROBOTSTXT_OBEY功能,在setting找到這個變量,設(shè)置為False即可解決。
# Obey robots.txt rules
ROBOTSTXT_OBEY = False
然后再調(diào)試,還是有問題,如下:

于是 想起來可能是因為這個reward_url網(wǎng)頁出錯,但是里邊的json數(shù)據(jù)還是有的,嘗試改一下reward_url地址中?count=20改為.json如下:
reward_url = ['http://www.itdecent.cn/notes/{}/rewards.json'.format(str(id))]
運行成功!
專題也是異步加載,所以方法和打賞數(shù)據(jù)處理一樣。