Python使用Srapy框架爬虫模拟登陆并抓取知乎内容


Posted in Python onJuly 02, 2016

一、Cookie原理
HTTP是无状态的面向连接的协议, 为了保持连接状态, 引入了Cookie机制
Cookie是http消息头中的一种属性,包括:

  • Cookie名字(Name)Cookie的值(Value)
  • Cookie的过期时间(Expires/Max-Age)
  • Cookie作用路径(Path)
  • Cookie所在域名(Domain),使用Cookie进行安全连接(Secure)

前两个参数是Cookie应用的必要条件,另外,还包括Cookie大小(Size,不同浏览器对Cookie个数及大小限制是有差异的)。

二、模拟登陆
这次主要爬取的网站是知乎
爬取知乎就需要登陆的, 通过之前的python内建库, 可以很容易的实现表单提交。

现在就来看看如何通过Scrapy实现表单提交。

首先查看登陆时的表单结果, 依然像前面使用的技巧一样, 故意输错密码, 方面抓到登陆的网页头部和表单(我使用的Chrome自带的开发者工具中的Network功能)

Python使用Srapy框架爬虫模拟登陆并抓取知乎内容

查看抓取到的表单可以发现有四个部分:

  • 邮箱和密码就是个人登陆的邮箱和密码
  • rememberme字段表示是否记住账号
  • 第一个字段是_xsrf,猜测是一种验证机制
  • 现在只有_xsrf不知道, 猜想这个验证字段肯定会实现在请求网页的时候发送过来, 那么我们查看当前网页的源码(鼠标右键然后查看网页源代码, 或者直接用快捷键)

Python使用Srapy框架爬虫模拟登陆并抓取知乎内容

发现我们的猜测是正确的

那么现在就可以来写表单登陆功能了

def start_requests(self):
    return [Request("https://www.zhihu.com/login", callback = self.post_login)] #重写了爬虫类的方法, 实现了自定义请求, 运行成功后会调用callback回调函数

  #FormRequeset
  def post_login(self, response):
    print 'Preparing login'
    #下面这句话用于抓取请求网页后返回网页中的_xsrf字段的文字, 用于成功提交表单
    xsrf = Selector(response).xpath('//input[@name="_xsrf"]/@value').extract()[0]
    print xsrf
    #FormRequeset.from_response是Scrapy提供的一个函数, 用于post表单
    #登陆成功后, 会调用after_login回调函数
    return [FormRequest.from_response(response,  
              formdata = {
              '_xsrf': xsrf,
              'email': '123456',
              'password': '123456'
              },
              callback = self.after_login
              )]

其中主要的功能都在函数的注释中说明
三、Cookie的保存
为了能使用同一个状态持续的爬取网站, 就需要保存cookie, 使用cookie保存状态, Scrapy提供了cookie处理的中间件, 可以直接拿来使用

CookiesMiddleware:

这个cookie中间件保存追踪web服务器发出的cookie, 并将这个cookie在接来下的请求的时候进行发送
Scrapy官方的文档中给出了下面的代码范例 :

for i, url in enumerate(urls):
  yield scrapy.Request("http://www.example.com", meta={'cookiejar': i},
    callback=self.parse_page)

def parse_page(self, response):
  # do some processing
  return scrapy.Request("http://www.example.com/otherpage",
    meta={'cookiejar': response.meta['cookiejar']},
    callback=self.parse_other_page)

那么可以对我们的爬虫类中方法进行修改, 使其追踪cookie

#重写了爬虫类的方法, 实现了自定义请求, 运行成功后会调用callback回调函数
  def start_requests(self):
    return [Request("https://www.zhihu.com/login", meta = {'cookiejar' : 1}, callback = self.post_login)] #添加了meta

  #FormRequeset出问题了
  def post_login(self, response):
    print 'Preparing login'
    #下面这句话用于抓取请求网页后返回网页中的_xsrf字段的文字, 用于成功提交表单
    xsrf = Selector(response).xpath('//input[@name="_xsrf"]/@value').extract()[0]
    print xsrf
    #FormRequeset.from_response是Scrapy提供的一个函数, 用于post表单
    #登陆成功后, 会调用after_login回调函数
    return [FormRequest.from_response(response,  #"http://www.zhihu.com/login",
              meta = {'cookiejar' : response.meta['cookiejar']}, #注意这里cookie的获取
              headers = self.headers,
              formdata = {
              '_xsrf': xsrf,
              'email': '123456',
              'password': '123456'
              },
              callback = self.after_login,
              dont_filter = True
              )]

四、伪装头部
有时候登陆网站需要进行头部伪装, 比如增加防盗链的头部, 还有模拟服务器登陆

Python使用Srapy框架爬虫模拟登陆并抓取知乎内容

为了保险, 我们可以在头部中填充更多的字段, 如下

headers = {
  "Accept": "*/*",
  "Accept-Encoding": "gzip,deflate",
  "Accept-Language": "en-US,en;q=0.8,zh-TW;q=0.6,zh;q=0.4",
  "Connection": "keep-alive",
  "Content-Type":" application/x-www-form-urlencoded; charset=UTF-8",
  "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.111 Safari/537.36",
  "Referer": "http://www.zhihu.com/"
  }

在scrapy中Request和FormRequest初始化的时候都有一个headers字段, 可以自定义头部, 这样我们可以添加headers字段

形成最终版的登陆函数

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from scrapy.contrib.spiders import CrawlSpider, Rule
from scrapy.selector import Selector
from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor
from scrapy.http import Request, FormRequest
from zhihu.items import ZhihuItem



class ZhihuSipder(CrawlSpider) :
  name = "zhihu"
  allowed_domains = ["www.zhihu.com"]
  start_urls = [
    "http://www.zhihu.com"
  ]
  rules = (
    Rule(SgmlLinkExtractor(allow = ('/question/\d+#.*?', )), callback = 'parse_page', follow = True),
    Rule(SgmlLinkExtractor(allow = ('/question/\d+', )), callback = 'parse_page', follow = True),
  )
  headers = {
  "Accept": "*/*",
  "Accept-Encoding": "gzip,deflate",
  "Accept-Language": "en-US,en;q=0.8,zh-TW;q=0.6,zh;q=0.4",
  "Connection": "keep-alive",
  "Content-Type":" application/x-www-form-urlencoded; charset=UTF-8",
  "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.111 Safari/537.36",
  "Referer": "http://www.zhihu.com/"
  }

  #重写了爬虫类的方法, 实现了自定义请求, 运行成功后会调用callback回调函数
  def start_requests(self):
    return [Request("https://www.zhihu.com/login", meta = {'cookiejar' : 1}, callback = self.post_login)]

  #FormRequeset出问题了
  def post_login(self, response):
    print 'Preparing login'
    #下面这句话用于抓取请求网页后返回网页中的_xsrf字段的文字, 用于成功提交表单
    xsrf = Selector(response).xpath('//input[@name="_xsrf"]/@value').extract()[0]
    print xsrf
    #FormRequeset.from_response是Scrapy提供的一个函数, 用于post表单
    #登陆成功后, 会调用after_login回调函数
    return [FormRequest.from_response(response,  #"http://www.zhihu.com/login",
              meta = {'cookiejar' : response.meta['cookiejar']},
              headers = self.headers, #注意此处的headers
              formdata = {
              '_xsrf': xsrf,
              'email': '1095511864@qq.com',
              'password': '123456'
              },
              callback = self.after_login,
              dont_filter = True
              )]

  def after_login(self, response) :
    for url in self.start_urls :
      yield self.make_requests_from_url(url)

  def parse_page(self, response):
    problem = Selector(response)
    item = ZhihuItem()
    item['url'] = response.url
    item['name'] = problem.xpath('//span[@class="name"]/text()').extract()
    print item['name']
    item['title'] = problem.xpath('//h2[@class="zm-item-title zm-editable-content"]/text()').extract()
    item['description'] = problem.xpath('//div[@class="zm-editable-content"]/text()').extract()
    item['answer']= problem.xpath('//div[@class=" zm-editable-content clearfix"]/text()').extract()
    return item

五、Item类和抓取间隔
完整的知乎爬虫代码链接

from scrapy.item import Item, Field


class ZhihuItem(Item):
  # define the fields for your item here like:
  # name = scrapy.Field()
  url = Field() #保存抓取问题的url
  title = Field() #抓取问题的标题
  description = Field() #抓取问题的描述
  answer = Field() #抓取问题的答案
  name = Field() #个人用户的名称

设置抓取间隔, 访问由于爬虫的过快抓取, 引发网站的发爬虫机制, 在setting.py中设置

BOT_NAME = 'zhihu'

SPIDER_MODULES = ['zhihu.spiders']
NEWSPIDER_MODULE = 'zhihu.spiders'
DOWNLOAD_DELAY = 0.25  #设置下载间隔为250ms

更多设置可以查看官方文档

抓取结果(只是截取了其中很少一部分)

...
 'url': 'http://www.zhihu.com/question/20688855/answer/16577390'}
2014-12-19 23:24:15+0800 [zhihu] DEBUG: Crawled (200) <GET http://www.zhihu.com/question/20688855/answer/15861368> (referer: http://www.zhihu.com/question/20688855/answer/19231794)
[]
2014-12-19 23:24:15+0800 [zhihu] DEBUG: Scraped from <200 http://www.zhihu.com/question/20688855/answer/15861368>
  {'answer': [u'\u9009\u4f1a\u8ba1\u8fd9\u4e2a\u4e13\u4e1a\uff0c\u8003CPA\uff0c\u5165\u8d22\u52a1\u8fd9\u4e2a\u884c\u5f53\u3002\u8fd9\u4e00\u8def\u8d70\u4e0b\u6765\uff0c\u6211\u53ef\u4ee5\u5f88\u80af\u5b9a\u7684\u544a\u8bc9\u4f60\uff0c\u6211\u662f\u771f\u7684\u559c\u6b22\u8d22\u52a1\uff0c\u70ed\u7231\u8fd9\u4e2a\u884c\u4e1a\uff0c\u56e0\u6b64\u575a\u5b9a\u4e0d\u79fb\u5730\u5728\u8fd9\u4e2a\u884c\u4e1a\u4e2d\u8d70\u4e0b\u53bb\u3002',
        u'\u4e0d\u8fc7\u4f60\u8bf4\u6709\u4eba\u4ece\u5c0f\u5c31\u559c\u6b22\u8d22\u52a1\u5417\uff1f\u6211\u89c9\u5f97\u51e0\u4e4e\u6ca1\u6709\u5427\u3002\u8d22\u52a1\u7684\u9b45\u529b\u5728\u4e8e\u4f60\u771f\u6b63\u61c2\u5f97\u5b83\u4e4b\u540e\u3002',
        u'\u901a\u8fc7\u5b83\uff0c\u4f60\u53ef\u4ee5\u5b66\u4e60\u4efb\u4f55\u4e00\u79cd\u5546\u4e1a\u7684\u7ecf\u8425\u8fc7\u7a0b\uff0c\u4e86\u89e3\u5176\u7eb7\u7e41\u5916\u8868\u4e0b\u7684\u5b9e\u7269\u6d41\u3001\u73b0\u91d1\u6d41\uff0c\u751a\u81f3\u4f60\u53ef\u4ee5\u638c\u63e1\u5982\u4f55\u53bb\u7ecf\u8425\u8fd9\u79cd\u5546\u4e1a\u3002',
        u'\u5982\u679c\u5bf9\u4f1a\u8ba1\u7684\u8ba4\u8bc6\u4ec5\u4ec5\u505c\u7559\u5728\u505a\u5206\u5f55\u8fd9\u4e2a\u5c42\u9762\uff0c\u5f53\u7136\u4f1a\u89c9\u5f97\u67af\u71e5\u65e0\u5473\u3002\u5f53\u4f60\u5bf9\u5b83\u7684\u8ba4\u8bc6\u8fdb\u5165\u5230\u6df1\u5c42\u6b21\u7684\u65f6\u5019\uff0c\u4f60\u81ea\u7136\u5c31\u4f1a\u559c\u6b22\u4e0a\u5b83\u4e86\u3002\n\n\n'],
   'description': [u'\u672c\u4eba\u5b66\u4f1a\u8ba1\u6559\u80b2\u4e13\u4e1a\uff0c\u6df1\u611f\u5176\u67af\u71e5\u4e4f\u5473\u3002\n\u5f53\u521d\u662f\u51b2\u7740\u5e08\u8303\u4e13\u4e1a\u62a5\u7684\uff0c\u56e0\u4e3a\u68a6\u60f3\u662f\u6210\u4e3a\u4e00\u540d\u8001\u5e08\uff0c\u4f46\u662f\u611f\u89c9\u73b0\u5728\u666e\u901a\u521d\u9ad8\u4e2d\u8001\u5e08\u5df2\u7ecf\u8d8b\u4e8e\u9971\u548c\uff0c\u800c\u987a\u6bcd\u4eb2\u5927\u4eba\u7684\u610f\u9009\u4e86\u8fd9\u4e2a\u4e13\u4e1a\u3002\u6211\u559c\u6b22\u4e0a\u6559\u80b2\u5b66\u7684\u8bfe\uff0c\u5e76\u597d\u7814\u7a76\u5404\u79cd\u6559\u80b2\u5fc3\u7406\u5b66\u3002\u4f46\u4f1a\u8ba1\u8bfe\u4f3c\u4e4e\u662f\u4e3b\u6d41\u3001\u54ce\u3002\n\n\u4e00\u76f4\u4e0d\u559c\u6b22\u94b1\u4e0d\u94b1\u7684\u4e13\u4e1a\uff0c\u6240\u4ee5\u5f88\u597d\u5947\u5927\u5bb6\u9009\u4f1a\u8ba1\u4e13\u4e1a\u5230\u5e95\u662f\u51fa\u4e8e\u4ec0\u4e48\u76ee\u7684\u3002\n\n\u6bd4\u5982\u8bf4\u5b66\u4e2d\u6587\u7684\u4f1a\u8bf4\u4ece\u5c0f\u559c\u6b22\u770b\u4e66\uff0c\u4f1a\u6709\u4ece\u5c0f\u559c\u6b22\u4f1a\u8ba1\u501f\u554a\u8d37\u554a\u7684\u7684\u4eba\u5417\uff1f'],
   'name': [],
   'title': [u'\n\n', u'\n\n'],
   'url': 'http://www.zhihu.com/question/20688855/answer/15861368'}
...

六、存在问题

  • Rule设计不能实现全网站抓取, 只是设置了简单的问题的抓取
  • Xpath设置不严谨, 需要重新思考
  • Unicode编码应该转换成UTF-8
Python 相关文章推荐
在Heroku云平台上部署Python的Django框架的教程
Apr 20 Python
Python使用reportlab将目录下所有的文本文件打印成pdf的方法
May 20 Python
基于Python实现文件大小输出
Jan 11 Python
Python实现数据库并行读取和写入实例
Jun 09 Python
Python参数解析模块sys、getopt、argparse使用与对比分析
Apr 02 Python
Python 50行爬虫抓取并处理图灵书目过程详解
Sep 20 Python
python3中numpy函数tile的用法详解
Dec 04 Python
tensorflow将图片保存为tfrecord和tfrecord的读取方式
Feb 17 Python
如何在django中运行scrapy框架
Apr 22 Python
Python Selenium实现无可视化界面过程解析
Aug 25 Python
python 实现性别识别
Nov 21 Python
python脚本定时发送邮件
Dec 22 Python
Ruby元编程基础学习笔记整理
Jul 02 #Python
Python的爬虫程序编写框架Scrapy入门学习教程
Jul 02 #Python
搭建Python的Django框架环境并建立和运行第一个App的教程
Jul 02 #Python
Python使用poplib模块和smtplib模块收发电子邮件的教程
Jul 02 #Python
python图片验证码生成代码
Jul 02 #Python
Python彩色化Linux的命令行终端界面的代码实例分享
Jul 02 #Python
Python使用迭代器打印螺旋矩阵的思路及代码示例
Jul 02 #Python
You might like
精美漂亮的php分页类代码
2013/04/02 PHP
PHP实现多维数组转字符串和多维数组转一维数组的方法
2015/08/08 PHP
tp5框架无刷新分页实现方法分析
2019/09/26 PHP
javascript web页面刷新的方法收集
2009/07/02 Javascript
javascript实现的在当前窗口中漂浮框的代码
2010/03/15 Javascript
为JavaScript添加重载函数的辅助方法
2010/07/04 Javascript
jquery实现文本框鼠标右击无效以及不能输入的代码
2010/11/05 Javascript
jQuery中RadioButtonList的功能及用法实例介绍
2013/08/23 Javascript
jquery定时滑出可最小化的底部提示层特效代码
2013/10/02 Javascript
fixedBox固定div漂浮代码支持ie6以上大部分主流浏览器
2014/06/26 Javascript
javascript学习指南之回调问题
2016/04/23 Javascript
jquery.masonry瀑布流效果
2017/05/25 jQuery
详解如何使用node.js的开发框架express创建一个web应用
2018/12/20 Javascript
html2canvas属性和使用方法以及如何使用html2canvas将HTML内容写入Canvas生成图片
2020/01/12 Javascript
解决微信授权成功后点击按返回键出现空白页和报错的问题
2020/06/08 Javascript
vue实现图片上传到后台
2020/06/29 Javascript
Python2.x中str与unicode相关问题的解决方法
2015/03/30 Python
Python多线程编程(八):使用Event实现线程间通信
2015/04/05 Python
python对DICOM图像的读取方法详解
2017/07/17 Python
python3+PyQt5实现支持多线程的页面索引器应用程序
2018/04/20 Python
python在html中插入简单的代码并加上时间戳的方法
2018/10/16 Python
python3利用ctypes传入一个字符串类型的列表方法
2019/02/12 Python
关于Pytorch的MLP模块实现方式
2020/01/07 Python
TensorFlow:将ckpt文件固化成pb文件教程
2020/02/11 Python
python爬虫scrapy图书分类实例讲解
2020/11/23 Python
CSS3中background-clip和background-origin的区别示例介绍
2014/03/10 HTML / CSS
印尼美容产品购物网站:PerfectBeauty.id
2017/12/01 全球购物
Michael Kors加拿大官网:购买设计师手袋、手表、鞋子、服装等
2019/03/16 全球购物
高中毕业生自我鉴定
2013/11/03 职场文书
行政求职信
2014/07/04 职场文书
2014年教师节红领巾广播稿
2014/09/10 职场文书
甲乙双方合作协议书
2014/10/13 职场文书
毕业设计指导教师评语
2014/12/30 职场文书
财务工作个人总结
2015/02/27 职场文书
党员读书活动心得体会
2016/01/14 职场文书
Python数据处理的三个实用技巧分享
2022/04/01 Python