
開篇.jpg
環(huán)境:win7、pycharm、python3.5
關鍵字:scrapy1.4、mongodb
本文主要分享用scrapy1.4實現(xiàn)知乎模擬郵箱登陸以及將根據(jù)rules匹配的url鏈接爬取(詳見下文zhihu_login.py)數(shù)據(jù)并存入mongodb。
在做之前閱讀了這兩篇文章Python爬蟲(七)--Scrapy模擬登錄 和Scrapy模擬登陸知乎,但是未能運行成功,文中也沒有處理驗證碼。不過,實現(xiàn)原理大同小異,大家可以參考,本文主要簡單實現(xiàn)并且正確運行,有什么問題還望大家多多指教。謝謝^^
獲取源碼請戳我
下面正文開始:

scrapy_architecture.png
1.cmd命令行下新建項目,詳情請戳我
scrapy startproject zhihu_login_byscrapy
目錄結(jié)構如下:
zhihu_login_byscrapy/
scrapy.cfg #項目的配置文件
zhihu_login_byscrapy/
__init__.py
items.py # 保存爬取到的數(shù)據(jù)的容器
pipelines.py # 項目 pipelines 文件 實現(xiàn)存儲
settings.py # 項目的設置文件
spiders/ # 這里寫你的爬蟲程序
__init__.py

目錄結(jié)構.png
2.模擬登陸并爬取數(shù)據(jù)zhihu_login.py
在運行前請將代碼中的password 和email 替換
主要邏輯分為:
- 獲取_xsrf
- 獲取驗證碼
- 傳遞入?yún)?shù)post請求處理
- ItemLoader解析數(shù)據(jù)
涉及的知識可參考官方文檔 +Rule使用+ItemLoader詳解
完整代碼及其注釋如下:
from scrapy.spiders import CrawlSpider, Rule
from scrapy.selector import Selector
from scrapy.linkextractors import LinkExtractor
from scrapy.http import Request, FormRequest
from zhihu_login_byscrapy.items import ZhihuLoginByscrapyItem
import json,time
from PIL import Image
from scrapy.loader import ItemLoader
class ZhihuLoginByscrapy(CrawlSpider):
name = 'zhihu_scrapy' #唯一值
allowed_domains = ["zhihu.com"]
start_urls = [
"https://www.zhihu.com"
]
#自動從response中根據(jù)正則表達式提取url,再根據(jù)這個url再次發(fā)起請求,并用callback解析返回的結(jié)果
#follow修改為True,那么爬蟲會start_urls爬取的頁面中在尋找符合規(guī)則的url,如此循環(huán),直到把全站爬取完畢。
rules = (
Rule(LinkExtractor(allow=('/question/\d+#.*?', )),callback='parse_question',follow=True),
Rule(LinkExtractor(allow=('/question/\d+',)), callback='parse_question', follow=True),
)
#請求頭
headers = {
'Accept':'* / *',
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "zh-CN,zh;q=0.8",
"Connection": "keep-alive",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"Host": "www.zhihu.com",
"Referer": "https://www.zhihu.com/",
}
#程序入口
def start_requests(self):
return [Request("https://www.zhihu.com/login/email",headers = self.headers,meta={"cookiejar":1},callback=self.post_login)]
def post_login(self,response):
#獲取_xsrf
_xsrf = Selector(response).xpath('//input[@name="_xsrf"]/@value').extract()[0]
if _xsrf:
formdata = {
'_xsrf': _xsrf,
'password': '*********',
'remember_me': 'true',
'email': '******@*****.com',
'captcha': ''
}
t = str(int(time.time() * 1000))
captcha_url = 'http://www.zhihu.com/captcha.gif?r=' + t + "&type=login"
return [Request(captcha_url, headers=self.headers, meta={"cookiejar": response.meta['cookiejar'], "formdata": formdata},
callback=self.parse_captcha)]
#獲取驗證碼
def parse_captcha(self, response):
with open('captcha.jpg', 'wb') as f:
f.write(response.body)
# Pillow顯示驗證碼
img = Image.open('captcha.jpg')
img.show()
captcha = input('請需要輸入驗證碼: ')
formdata = response.meta['formdata']
formdata['captcha'] = captcha
#登陸成功后, 會調(diào)用after_login回調(diào)函數(shù)
return [FormRequest("https://www.zhihu.com/login/email",
meta={'cookiejar': response.meta['cookiejar']},
method='POST',
headers=self.headers,
formdata=formdata,
callback=self.after_login,
dont_filter = True,
)]
#程序自動使用start_urls構造Request并發(fā)送請求,然后調(diào)用parse函數(shù)對其進行解析,在這個解析過程中使用rules中的規(guī)則從html(或xml)文本中提取匹配的鏈接,通過這個鏈接再次生成Request,如此不斷循環(huán),直到返回的文本中再也沒有匹配的鏈接,或調(diào)度器中的Request對象用盡,程序才停止。
def after_login(self,response):
for url in self.start_urls:
yield Request(url, headers=self.headers, meta={'cookiejar': response.meta['cookiejar']}, dont_filter=True)
def parse_question(self, response):
self.logger.info('Hi, this is an item page! %s', response.url)
item = ItemLoader(item=ZhihuLoginByscrapyItem(), response=response)
#傳遞 Item 類的字段名稱和對應的 css 解析語法
item.add_value('url',response.url)
item.add_css('title','h1.QuestionHeader-title::text')
item.add_xpath('read', '(//div[@class="NumberBoard-value"])[1]/text()')
item.add_xpath('focus', '(//div[@class="NumberBoard-value"])[2]/text()')
item.add_xpath('description', '//span[@class="RichText CopyrightRichText-richText"]/text()')
print(item)
return item.load_item()
3.在items.py中定義爬取到數(shù)據(jù)的容器,簡言之就是定義爬取到的數(shù)據(jù)字段,以匹配到的此鏈接為例,獲取問題的url、標題、關注者、瀏覽量、問題描述。
涉及的知識可參考input/output_processor+使用 Item 類轉(zhuǎn)換傳輸數(shù)據(jù)以及ItemLoader 機制解析
完整代碼及其注釋如下(還可以優(yōu)化):
import scrapy
from scrapy.loader.processors import TakeFirst,MapCompose,Join
class ZhihuLoginByscrapyItem(scrapy.Item):
# define the fields for your item here like:
url = scrapy.Field(
output_processor=Join(),
) # 問題url
title = scrapy.Field(
output_processor=Join(),
) # 問題標題
focus = scrapy.Field(
output_processor=Join(),
) # 問題關注者有多少
read = scrapy.Field(
output_processor=Join(),
) # 問題瀏覽多少
description = scrapy.Field(
output_processor=Join(),
) # 問題描述
- pipelines.py中將數(shù)據(jù)存儲到json和mongodb中
涉及的知識可參考: 使用 Pipeline 保存數(shù)據(jù) + 官方文檔mongodb案例+JsonItemExporter參考
from scrapy.exporters import JsonItemExporter
import pymongo
class ZhihuLoginByscrapyPipeline(object):
# 調(diào)用 scrapy 提供的 json exporter 導出 json 文件
def __init__(self):
self.file = open('zhihu_exporter.json', 'wb')
# 初始化 exporter 實例,執(zhí)行輸出的文件和編碼
self.exporter = JsonItemExporter(self.file, encoding='utf-8', ensure_ascii=False)
# 開啟導出
self.exporter.start_exporting()
def close_spider(self, spider):
self.exporter.finish_exporting()
self.file.close()
# 將 Item 實例導出到 json 文件
def process_item(self, item, spider):
self.exporter.export_item(item)
return item
class MongoByscrapyPipeline(object):
def __init__(self, mongo_uri, mongo_db,mongo_col):
self.mongo_uri = mongo_uri
self.mongo_db = mongo_db
self.mongo_col = mongo_col
@classmethod
def from_crawler(cls, crawler):
return cls(
mongo_uri=crawler.settings.get('MONGO_URI'),
mongo_db=crawler.settings.get('MONGODB_DB'),
mongo_col=crawler.settings.get('MONGODB_COLLECTION')
)
def open_spider(self, spider):
self.client = pymongo.MongoClient(self.mongo_uri)
self.db = self.client[self.mongo_db]
def close_spider(self, spider):
# 釋放資源
self.client.close()
def process_item(self, item, spider):
self.db[self.mongo_col].insert_one(dict(item))
return item
5.settings.py添加如下內(nèi)容
涉及的知識可參考官方文檔settings
#修改USER_AGEN
USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36'
DOWNLOAD_DELAY = 4 #下載延遲
#PIPELINES配置,后面數(shù)字代表優(yōu)先級
ITEM_PIPELINES = {
'zhihu_login_byscrapy.pipelines.MongoByscrapyPipeline': 300,
'zhihu_login_byscrapy.pipelines.ZhihuLoginByscrapyPipeline': 800,
}
#mongodb配置
MONGODB_SERVER = "localhost"
MONGODB_PORT = 27017
MONGODB_DB = "ana_zhihu"
MONGODB_COLLECTION = "zhihu_scrapy"
6.以上就是涉及到的代碼,下面就是pycharm中執(zhí)行我們的scrapy程序,新建begin.py文件,首先需要按圖示配置好,Script處指定執(zhí)行腳本,最后點擊執(zhí)行即可~
配置.png
begin.py
from scrapy import cmdline
cmdline.execute('scrapy crawl zhihu_scrapy '.split())
7.最后運行效果如下
控制臺print出的item.png

mongodb中存儲的數(shù)據(jù).png

存儲的json數(shù)據(jù).png
8.以上就是所有內(nèi)容,但是尚有需要完善的地方,比如模擬手機登陸、爬蟲job的暫停&執(zhí)行、中間件的使用、代碼優(yōu)化等。