爬虫学习之基于Scrapy的自动登录

概述


在前面两篇(爬虫学习之基于Scrapy的网络爬虫爬虫学习之简单的网络爬虫)文章中我们通过两个实际的案例,采用不同的方式进行了内容提取。我们对网络爬虫有了一个比较初级的认识,只要发起请求获取响应的网页内容,然后对内容进行格式化存储。很多时候我们抓取到的内容可能会发生重复,也有可能是需要计算或者组织过的全新的内容甚至是需要登录后才能访问的内容, 那么这一篇我们来学习一下Scrapy的Item部分以及了解如何使用Scrapy来进行自动登录。

起步


首先我们使用Scrapy的命令行创建一个新的项目

scrapy startproject douban

运行后,我们就有了下面这样的目录结构

+ douban                               # 根目录
    |- douban                          # Python的项目目录
        |- spiders                     # 爬虫Spider部分,用于提取网页内容
            |- __init__.py
        |- __init__.py
        |- items.py                    # 爬虫item, 用于定义数据结构
        |- pipelines.py                # 爬虫pipeline,用于处理提取的结构,比如清洗数据、去重等
        |- settings.py                 # Scrapy框架参数项目参数设置
    |- scrapy.cfg                      # 爬虫部署相关设置

Scrapy为我们生成了已经组织好的目录结构,上面的注释部分解释了每个文件及目录的作用。

建立目标


本篇我们来建立两个目标,这两个目标都是基于豆瓣网

目标一:抓取豆瓣TOP250的图书信息并保存成csv文件
目标二:抓取我的第一页豆邮标题(需要登录),并保存成csv文件

分析目标一


目标一是豆瓣的TOP250图书信息,首先我们进入到TOP250的列表(https://book.douban.com/top250) ,我用图示圈出我们这次要爬取的内容,具体请看图示:

豆瓣图书TOP250

从图上的框线中我们主要圈出了书名、价格、出版年份、出版社、评分,其中出版年份,出版社以及价格是在一行中,这个我们需要进一步处理。
分页的处理:总记录是250条,每页是25条图书信息,共分了10页

实现目标一


需要用到的概念:

Item
Item Pipeline

首先建立Scrapy的Item, Scrapy的Item就是我们需要存储的数据结构,先修改items, 然后在spiders目录中新建一个名为bookspider.py的Python文件,由于我们需要在一堆字符串中提取出出版社和价格等信息所以我们这里需要对抓取的内容进一步处理, 在这之前还需要修改settings.py文件:

  1. 加入faker的模拟USER_AGENT数据防止被豆瓣屏蔽,
  2. 也可以设置DEFAULT_REQUEST_HEADERS参数。
  3. 修改ITEM_PIPELINES

代码如下所示:

items.py

# -*- coding: utf-8 -*-
'''by sudo rm -rf http://imchenkun.com'''
import scrapy


class DoubanBookItem(scrapy.Item):
    name = scrapy.Field()            # 书名
    price = scrapy.Field()           # 价格
    edition_year = scrapy.Field()    # 出版年份
    publisher = scrapy.Field()       # 出版社
    ratings = scrapy.Field()         # 评分

bookspider.py

# -*- coding:utf-8 -*-
'''by sudo rm -rf http://imchenkun.com'''
import scrapy
from douban.items import DoubanBookItem


class BookSpider(scrapy.Spider):
    name = 'douban-book'
    allowed_domains = ['douban.com']
    start_urls = [
        'https://book.douban.com/top250'
    ]

    def parse(self, response):
        # 请求第一页
        yield scrapy.Request(response.url, callback=self.parse_next)

        # 请求其它页
        for page in response.xpath('//div[@class="paginator"]/a'):
            link = page.xpath('@href').extract()[0]
            yield scrapy.Request(link, callback=self.parse_next)

    def parse_next(self, response):
        for item in response.xpath('//tr[@class="item"]'):
            book = DoubanBookItem()
            book['name'] = item.xpath('td[2]/div[1]/a/@title').extract()[0]
            book['price'] = item.xpath('td[2]/p/text()').extract()[0]
            book['ratings'] = item.xpath('td[2]/div[2]/span[2]/text()').extract()[0]
            yield book

pipelines.py

# -*- coding: utf-8 -*-
'''by sudo rm -rf http://imchenkun.com'''


class DoubanBookPipeline(object):
    def process_item(self, item, spider):
        info = item['price'].split(' / ')  # [法] 圣埃克苏佩里 / 马振聘 / 人民文学出版社 / 2003-8 / 22.00元
        item['name'] = item['name']
        item['price'] = info[-1]
        item['edition_year'] = info[-2]
        item['publisher'] = info[-3]
        return item

最后我们到douban的根目录中执行以下命令来运行爬虫来执行并导出数据到csv文件

scrapy crawl douban-book -o douban_book_top250.csv

csv文件截图如下:


导出的图书数据

分析目标二


目标二是建立在理解了目标一的基础上进行的,因为豆瓣登录次数过多会有验证码出现,这里提供一种手工填写验证码的方式,暂时不讨论如何去识别验证码,目标二的核心概念是如何提交POST表单和登录成功后带Cookie的请求。首先我们可以看到页面结构如下图所示:


豆瓣邮件列表

实现目标二


定义Item

# -*- coding: utf-8 -*-import scrapy
'''by sudo rm -rf  http://imchenkun.com'''


class DoubanMailItem(scrapy.Item):
    sender_time = scrapy.Field()     # 发送时间
    sender_from = scrapy.Field()     # 发送人
    url = scrapy.Field()             # 豆邮详细地址
    title = scrapy.Field()           # 豆邮标题

定义doumailspider

# -*- coding:utf-8 -*-
'''by sudo rm -rf  http://imchenkun.com'''
import scrapy
from faker import Factory
from douban.items import DoubanMailItem
import urlparse

f = Factory.create()


class MailSpider(scrapy.Spider):
    name = 'douban-mail'
    allowed_domains = ['accounts.douban.com', 'douban.com']
    start_urls = [
        'https://www.douban.com/'
    ]
    headers = {
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
        'Accept-Encoding': 'gzip, deflate, br',
        'Accept-Language': 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3',
        'Connection': 'keep-alive',
        'Host': 'accounts.douban.com',
        'User-Agent': f.user_agent()
    }

    formdata = {
        'form_email': '您的账号',
        'form_password': '您的密码',
        # 'captcha-solution': '',
        # 'captcha-id': '',
        'login': '登录',
        'redir': 'https://www.douban.com/',
        'source': 'None'
    }

    def start_requests(self):
        return [scrapy.Request(url='https://www.douban.com/accounts/login',
                               headers=self.headers,
                               meta={'cookiejar': 1},
                               callback=self.parse_login)]

    def parse_login(self, response):
        # 如果有验证码要人为处理
        if 'captcha_image' in response.body:
            print 'Copy the link:'
            link = response.xpath('//img[@class="captcha_image"]/@src').extract()[0]
            print link
            captcha_solution = raw_input('captcha-solution:')
            captcha_id = urlparse.parse_qs(urlparse.urlparse(link).query, True)['id']
            self.formdata['captcha-solution'] = captcha_solution
            self.formdata['captcha-id'] = captcha_id
        return [scrapy.FormRequest.from_response(response,
                                                 formdata=self.formdata,
                                                 headers=self.headers,
                                                 meta={'cookiejar': response.meta['cookiejar']},
                                                 callback=self.after_login
                                                 )]

    def after_login(self, response):
        print response.status
        self.headers['Host'] = "www.douban.com"
        return scrapy.Request(url='https://www.douban.com/doumail/',
                              meta={'cookiejar': response.meta['cookiejar']},
                              headers=self.headers,
                              callback=self.parse_mail)

    def parse_mail(self, response):
        print response.status
        for item in response.xpath('//div[@class="doumail-list"]/ul/li'):
            mail = DoubanMailItem()
            mail['sender_time'] = item.xpath('div[2]/div/span[1]/text()').extract()[0]
            mail['sender_from'] = item.xpath('div[2]/div/span[2]/text()').extract()[0]
            mail['url'] = item.xpath('div[2]/p/a/@href').extract()[0]
            mail['title'] = item.xpath('div[2]/p/a/text()').extract()[0]
            print mail
            yield mail

这里需要注意的有三个地方:

  1. 第一个是meta中的cookiejar

Scrapy通过使用 cookiejar
Request meta key来支持单spider追踪多cookie session。默认情况下其使用一个cookie jar(session),不过您可以传递一个标示符来使用多个。

  1. start_requests 我们这里重写了爬虫爬取得第一个页面,这里一开始就进去到登录页面
  2. 当执行爬虫的时候,我们需要把打印出来的验证码地址粘贴到浏览器中,手动输入到控制上完成验证。

同目标一一样需要设置settings中的相关参数,唯一不同的是ITEM_PIPELINES。

最后我们使用以下命令来启动爬虫

scrapy crawl douban-mail -o douban_mail_page1.csv

csv文件截图如下:


豆邮数据

Github地址:https://github.com/imchenkun/ick-spider/tree/master/douban

总结


本篇我们学习了如果定义Item以及如何对Item进行进一步处理(Item Pipeline), 还通过登录豆瓣的案例来了解了如果使用Scrapy进行表单提交和Cookie追踪,也了解了对于有验证码的情况该如何处理,当然我们这里暂时还不讨论如何识别验证码。关于Scrapy的更高级的一些用法和特性可以进一步阅读Scrapy官网的文档。

特别申明:本文所提到的豆瓣网只是拿来进行爬虫的技术交流学习,读者涉及到的所有侵权问题都与本人无关,也希望大家在学习实战的过程中不要大量的爬取内容对服务器造成负担

本文首发在sudo rm -rf 采用署名(BY)-非商业性使用(NC)-禁止演绎(ND) 转载请注明原作者

--EOF--

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容