开篇.jpg
环境:win7、pycharm、python3.5
关键字:scrapy1.4、mongodb
本文主要分享用scrapy1.4实现知乎模拟邮箱登陆以及将根据rules匹配的url链接爬取(详见下文zhihu_login.py)数据并存入mongodb。
在做之前阅读了这两篇文章Python爬虫(七)--Scrapy模拟登录 和Scrapy模拟登陆知乎,但是未能运行成功,文中也没有处理验证码。不过,实现原理大同小异,大家可以参考,本文主要简单实现并且正确运行,有什么问题还望大家多多指教。谢谢^^
获取源码请戳我
下面正文开始:
scrapy_architecture.png
1.cmd命令行下新建项目,详情请戳我
scrapy startproject zhihu_login_byscrapy
目录结构如下:
zhihu_login_byscrapy/
scrapy.cfg #项目的配置文件
zhihu_login_byscrapy/
__init__.py
items.py # 保存爬取到的数据的容器
pipelines.py # 项目 pipelines 文件 实现存储
settings.py # 项目的设置文件
spiders/ # 这里写你的爬虫程序
__init__.py
目录结构.png
2.模拟登陆并爬取数据zhihu_login.py
在运行前请将代码中的password 和email 替换
主要逻辑分为:
- 获取_xsrf
- 获取验证码
- 传递入参数post请求处理
- ItemLoader解析数据
涉及的知识可参考官方文档 +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中根据正则表达式提取url,再根据这个url再次发起请求,并用callback解析返回的结果
#follow修改为True,那么爬虫会start_urls爬取的页面中在寻找符合规则的url,如此循环,直到把全站爬取完毕。
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
#登陆成功后, 会调用after_login回调函数
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并发送请求,然后调用parse函数对其进行解析,在这个解析过程中使用rules中的规则从html(或xml)文本中提取匹配的链接,通过这个链接再次生成Request,如此不断循环,直到返回的文本中再也没有匹配的链接,或调度器中的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中定义爬取到数据的容器,简言之就是定义爬取到的数据字段,以匹配到的此链接为例,获取问题的url、标题、关注者、浏览量、问题描述。
涉及的知识可参考input/output_processor+使用 Item 类转换传输数据以及ItemLoader 机制解析
完整代码及其注释如下(还可以优化):
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中将数据存储到json和mongodb中
涉及的知识可参考: 使用 Pipeline 保存数据 + 官方文档mongodb案例+JsonItemExporter参考
from scrapy.exporters import JsonItemExporter
import pymongo
class ZhihuLoginByscrapyPipeline(object):
# 调用 scrapy 提供的 json exporter 导出 json 文件
def __init__(self):
self.file = open('zhihu_exporter.json', 'wb')
# 初始化 exporter 实例,执行输出的文件和编码
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添加如下内容
涉及的知识可参考官方文档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配置,后面数字代表优先级
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中执行我们的scrapy程序,新建begin.py文件,首先需要按图示配置好,Script处指定执行脚本,最后点击执行即可~配置.png
begin.py
from scrapy import cmdline
cmdline.execute('scrapy crawl zhihu_scrapy '.split())
7.最后运行效果如下控制台print出的item.png
mongodb中存储的数据.png
存储的json数据.png
8.以上就是所有内容,但是尚有需要完善的地方,比如模拟手机登陆、爬虫job的暂停&执行、中间件的使用、代码优化等。