第二个爬虫

[TOC]

目标

实现爬虫的完整运行,登陆,js解析,url去重,通过中间件进行功能扩展,考虑验证码破解,页面更新

js解析,可以考虑Pyv8,PythonWebKit,Selenium,PhantomJS,Ghost.py等
数据存储,考虑用mongodb
去重,考虑用BitVector

完整的爬取

scrapy里,先定义了starturl,然后parse函数会自动去读取这些url,并做解析
可以通过request

Request(item['href'],callback=self.parescontent)

Scrapy uses Request and Response objects for crawling web sites.

Typically, Request objects are generated in the spiders and pass across the system until they reach the Downloader, which executes the request and returns a Response object which travels back to the spider that issued the request.

模拟登陆

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
                            )]

配置代理头

调用页面解析时候可以指定header头,requset的定义如下

class Request(object_ref):

    def __init__(self, url, callback=None, method='GET', headers=None, body=None,
                 cookies=None, meta=None, encoding='utf-8', priority=0,
                 dont_filter=False, errback=None):

        self._encoding = encoding  # this one has to be set first
        self.method = str(method).upper()
        self._set_url(url)
        self._set_body(body)
        assert isinstance(priority, int), "Request priority not an integer: %r" % priority
        self.priority = priority

        assert callback or not errback, "Cannot use errback without a callback"
        self.callback = callback
        self.errback = errback

        self.cookies = cookies or {}
        self.headers = Headers(headers or {}, encoding=encoding)
        self.dont_filter = dont_filter

        self._meta = dict(meta) if meta else None

实例化Request的时候,可以直接指定header

比如Request('http://www.baidu.com',callback=self.parescontent, method='GET', headers=sth, encoding='utf-8')

如果要配置多个header,并且自动配置也很简单,方式如下

import random
headers = [headera,headerb,headerc]
Request('http://www.baidu.com',callback=self.parescontent, method='GET', headers=random.choice(headers) , encoding='utf-8')

布隆过滤器及改进

布隆过滤器的思想 -- 不追求完美

在quora上,有个问题问,人们最常犯的错误是哪些,其中一个就是追求完美。
在it领域,为了让系统完美化,比如之前涉及存储系统,去重时想达到完美的标准,花的代价是2小时,如果稍加改动,可以让代价降低到1分钟左右,只有本来的百分之一不到。

布隆过滤器的思想,也是如此。

布隆过滤器的应用 - 使用案例

squid

squid里的cache digests 用的是布隆过滤器

chrom

chrom里判断恶意链接,也是用的布隆过滤器

hbase里也用了bloom filter

如图
bloom filter在hbase里的用法比较有意思,它先判断大小,再做bf,这样能让查询速度加快几十倍

布隆过滤器的缺点和改进

缺点

布隆过滤器的缺点是错判,就是说,不在里面的,可能判断成在里面,但是在里面的,一定不会错,而且,无法删除

改进

改进1 多bit

bloom filter其实就是bitmaq的改进,bitmap是单个hash,而bf是多个hash,本质上,都是一个bit只能存有或者没有两种状态
可以考虑用多bit记录的方式,这种方法,就是本来每个bit不是0就是1,现在可以记录其它的,如果add一个元素,把对应bit的数字加1即可
如果要del一个元素,对应位置的数字减1即可
好处是,可以删除元素
缺点是,可能会有位溢出,另外,错判还是会发生,速度也慢了

改进2 白名单

还有种改进方式是对一些常见的url加到白名单里
这种改进是不错的选择,对于某些不考虑过滤的url,可以先判断一下,剩下的url错判就错判,对结果影响是可以接受

布隆过滤器的细节 - 算法的实现

下面用pybloom演示一下布隆过滤器的用法

from pybloom import BloomFilter
from pybloom import benchmarks
f = BloomFilter(capacity=100000, error_rate=0.1)
# [f.add(x) for x in range(102)]
[f.add(x) for x in range(1001)]

for x in range(1001, 100000000):
    if x in f:
        print x

可以看出,布隆过滤器,还是比较高效的一种数据结构

存储

先安装monogdb,创建一个库叫lei,再建个表,叫做questions
然后在setings.py中配置mongodb的ip,port,db name,collection name这些

ITEM_PIPELINES = {
    'tutorial.pipelines.MongoDBPipeline': 300,
}
MONGODB_SERVER = "192.168.100.110"
MONGODB_PORT = 27017
MONGODB_DB = "lei"
MONGODB_COLLECTION = "questions"

# DOWNLOAD_DELAY = 5  # 抓取的延迟

然后建一个pipelines.py,这个pipelines是用来处理item的,把item解析后存到mongodb里,代码也很简单

import pymongo
from scrapy.conf import settings
from scrapy.exceptions import DropItem
from scrapy import log


class MongoDBPipeline(object):
    def __init__(self):
        connection = pymongo.MongoClient(
            settings['MONGODB_SERVER'],
            settings['MONGODB_PORT']
        )
        db = connection[settings['MONGODB_DB']]
        self.collection = db[settings['MONGODB_COLLECTION']]

    def process_item(self, item, spider):
        for data in item:
            if not data:
                raise DropItem("Missing data!")
        self.collection.update({'url': item['url']}, dict(item), upsert=True)
        log.msg("Question added to MongoDB database!",level=log.DEBUG, spider=spider)
        return item

执行scrapy crawl 51job,在robomon里即可看到爬取的结果

{
"_id" : ObjectId("57cfcebc2e45cfeb955b5483"),
"url" : "http://jobs.51job.com/nanjing-xwq/81462838.html?s=0",
"job_pay" : "6000-7999/月",
"company_type" : "民营公司",
"company_scale" : "50-150人",
"company_industry" : "影视/媒体/艺术/文化传播",
"job_describe" : "现招一名完美的男孩他要坐立笔直,言行端正。他要行动迅速,不出声响。他可以在大街上吹口哨,但在该保持安静的地方不吹口哨。他看起来要精神愉快,对每个人都笑脸相迎,从不生气。他要礼貌待人,尊重女人他愿意说一口纯正的普通话,而不是家乡话。他在与女孩的相处中不紧张。他和自己的母亲相处融洽,与她的关系最为亲近。他不虚伪,也不假正经,而是健康,快乐,充满活力。",
"job_title" : "现招一名完美的男孩 投资理财顾问助理总助"
}

遇到的问题

beautifulsoup解析页面,结果缺失的问题

在解析http://search.51job.com/list/070200,070201,0000,00,9,99,%2B,2,1.html时,这个页面的url一直解析不出来所有的url
一直以为是我代码写的有问题,坑了一上午,发现是解析器的问题 fire :-(

如果用bs4,载入时候html5不行,就换html,soup = BeautifulSoup(temp, "html.parser")

运行时报错

提示 AttributeError: 'list' object has no attribute 'iteritems'
原因是在settings文件里,配置item pipelines的时候,没有指定顺序,正确的方式如下:

ITEM_PIPELINES = {
'myproject.pipelines.PricePipeline': 300,
'myproject.pipelines.JsonWriterPipeline': 800,
}

items是根据数字大小,从低到高通过pipelines,数字的范围是0-1000

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

推荐阅读更多精彩内容