>

爬虫入门,scrapy爬虫踩的坑

- 编辑:www.bifa688.com -

爬虫入门,scrapy爬虫踩的坑

一.scrapy创建爬虫,cd进入爬虫的门类文件夹,输入:scrapy genspider zhihu0三www.zhihu.com

参谋资料:
Scrapy粤语文书档案 http://scrapy-chs.readthedocs.io/zh_CN/stable/index.html
Scrapy商讨斟酌连串 http://blog.csdn.net/u012150179/article/details/32343635

一、 前言

眼前学习了爬虫的两大框架,Pyspider 和Scrapy两者对初学者的话,依旧有非常的大的区其他,从难度上来说,Pyspider 较于Scrapy更为轻松,上手也越来越轻便,而Scrapy较于Pyspider 又进而的灵活多变,逻辑性越来越强,框架感拾足。具体来说,Pyspider有以下特征:

  • 支撑python 脚本,且可以放肆切换使用差异的解析库,举例lxml,beautifulsoup,默认为pyquery解析库
  • web 可视化的分界面,编写调节和测试可同时拓展监察和控制,查看历史音信以及运行景况
  • 支持MySQL、MongoDB等常用的数据库,和多少存款和储蓄工具
  • 能够爬取JS渲染的网页,并且在web分界面展现图片消息

民用认为相较于Pyspider的人性化设计,Scrapy则更像一个本领男应该具备的标配,因为在用它实行爬取的时候,你会意识到它结构框架的完整性,逻辑的严俊性,对代码的自主要编辑译调节性。它最重要分为以下多少个区块文本:

  • 以_name_独立命名的py主文件,其内含有宗旨的__name__Spider(Scrapy.spider)类,在其下有start_request(self)、parse_index(self,response)两大首要措施
  • Item.py 文本,其内的主要类为Item(Scrapy.Item),利用Scrapy 中的Field模块,在那个类个中来实行编辑所急需爬获得音讯
  • middleware.py 文本,则是针对性登录分界面、反爬网址来设定cookies池,以及反爬代码,其余还足以利logging模块来写入日志,实时监督爬虫输出
  • piplines.py 文本则是器重使用process_item(self,item,spider)来对供给爬取的剧情开始展览整治,以及选取MongoPipline()[以MongoDB为例]对爬取内容举办仓库储存
  • setting.py文本则更像是一个框架的按钮,能够启用cookies池,以及DEFAULT_REQUEST_HEADERS、ROBOTSTXT_OBEY

  展开pycharm开掘新扩展三个zhihu0三.py

scrapy使用相较于事先的urllib和requests四个爬虫库的运用依旧要复杂繁多,以为有个别简短的爬虫直接用requests BeautifulSoup来的更简便越来越快,为啥不用urllib?又是decode又是encode的挺麻烦,而requests则轻松了大多.而scrapy学会了后来选用起来倍感也比requests轻松方便些,难题是对于部分网址反爬虫的消除方案,将来那地点照旧学的很差,接下去以三个例证轻便询问记录下学习进程,具体scrapy学习还请参见下面给出的参照他事他说加以调查资料以及谷歌吧.

二、 Pyspyder 的为主使用

图片 1

壹. 创设爬虫项目

scrapy startproject collectips #创建一个爬取http://www.xicidaili.com/nn/上面代理ip的项目

创立项目后会自动生成3个索引,差不多如下,在那之中ips.csv是终极爬取的ip存款和储蓄的文件,一同初是未曾的

├── collectips
 ├── collectips
 │   ├── __init__.py
 │   ├── items.py    #设置要爬取的项目的内容部分
 │   ├── middlewares.py   #中间部件
 │   ├── pipelines.py     #对生成的item进行处理,若不设置,则默认为原网页爬取到的内容
 │   ├── __pycache__
 │   │   ├── __init__.cpython-35.pyc
 │   │   ├── items.cpython-35.pyc
 │   │   └── settings.cpython-35.pyc
 │   ├── settings.py    #每个项目都有这个文件,可对爬虫进行相关设置,如cookies,User-Agent等
 │   └── spiders     #编写爬虫主代码,此文件下可编写多个爬虫
 │       ├── __init__.py
 │       ├── ips.csv  #这是最后生成的文件,创建项目时是没有的
 │       ├── __pycache__
 │       │   ├── __init__.cpython-35.pyc
 │       │   └── xici.cpython-35.pyc
 │       └── xici.py     #编写爬虫代码的文件,非自动生成
 └── scrapy.cfg 

1.Pyspider 控制台

  1. 先是保障自个儿pip install pyspider 并且实行了情状变量的计划
  2. cmd--启用pyspider
  3. http://localhost:5000/ 进入web 交互分界面

 

二. 编纂items.py文件,填入想要爬取内容的称谓

# -*- coding: utf-8 -*-

# Define here the models for your scraped items
#
# See documentation in:
# http://doc.scrapy.org/en/latest/topics/items.html

import scrapy


class CollectipsItem(scrapy.Item):
    IP = scrapy.Field()      #要爬取的代理ip
    PORT = scrapy.Field()  #ip端口
    DNS_POSITION = scrapy.Field()  #服务器地址
    TYPE = scrapy.Field()    #ip类型,http或者https
    SPEED = scrapy.Field()    #ip响应速度
    LAST_CHECK_TIME = scrapy.Field()    #ip最后更新时间

2.Pyspider 的实例演示框架讲明(基于tripeadvisor网页)

class Handler(BaseHandler):
    crawl_config = {
    }
    client=pymongo.MongoClient('localhost')
    db=client['trip']
#首先利用该类方法的crawl_config 来进行存储变量的一个定义#

def on_start(self):
        self.crawl('https://www.tripadvisor.cn/Attractions-g186338-Activities-London_England.html', callback=self.index_page)
#这个方法跟Scrapy 中的start_request方法类似,主要是为了获取所要爬取的主界面,点击运行后,可以通过web 界面来观察主界面的情况#

    def index_page(self, response):
        for each in response.doc('#ATTR_ENTRY_ > div.attraction_clarity_cell > div > div > div.listing_info > div.listing_title > a').items():
            self.crawl(each.attr.href, callback=self.detail_page)
#利用 上述on_start(self)方法里面的回调函数来进入到该方法里面#
#其中response.doc(css selector).items()是pyquery的经典写法#
#其作用就是利用for 循环得到每一个详情页的连接#
#值得注意的是,Pspider里面提供了一个智能的css selector---enable css selector helper#
[点击所要寻找的css标识--自动圈出---右击箭头---复制到上述doc()里]
![show.png](https://upload-images.jianshu.io/upload_images/10516872-4bce304eec8eecb9.png?imageMogr2/auto-orient/strip|imageView2/2/w/1240)

def detail_page(self, response):#详情页的信息
        url=response.url
        name=response.doc('.heading_title').text()
        comment=response.doc('div > .more').text()
        address=response.doc('.colCnt3').text()
        phone=response.doc('.headerBL > div > .phone').text()

        return {
            "name": name,
            "commment": comment,
            "address":address,
            "phone":phone,
            "url":url,
        }
#利用response的自带url 属性可以得到每一个详情页的链接
#利用doc()、text()(pyquery解析库的常用方法,forexample:doc=pq(html),items=doc('.li-0 active).items()
for item in items:item.text() ,print item.text())
#其他的pquery 用法,比如silbing,children,parent,DOM,attr(href)/attr.href,不再赘述#
#其中上述代码doc内的css仍然可以通过enable css selector helper来自动获取十分方便#
#该方法自带return 元素返回,不用再像Scrapy框架一样,将其内容写进Item.py当中去,也是十分的方便,良心框架有木有!#

    def on_result(self,result):#自己定义一个result的方法,用于进行相关的存储
        if result:#如果从上面的detail_page里有相关的输出那么就进行存储
            self.save_to_mongo(result)

    def save_to_mongo(self,result):#将输出结果插入mongodb
        if self.db['london'].insert(result):
            print ('save to mongo success',result)
自行写入存储方法跟简单的爬虫一样的结构,并无差异,这一点也有区别于Scrapy 框架的setting.py和middlewares.py的整体框架结构

那么哪些促成翻页呢?------只需在上述index_page(self,response)里福寿无疆迭代就可以

next=response.doc('.pagination  .nav next').attr.href
        self.crawl(next,callback=self.index_page)
#回调自己实现,next为下一页索引链接,利用的正是pyquery解析库的attr(href)或者attr.href方法

整段代码如下:

from pyspider.libs.base_handler import *
import pymongo

class Handler(BaseHandler):
    crawl_config = {
    }
    client=pymongo.MongoClient('localhost')
    db=client['trip']

    @every(minutes=24 * 60)
    def on_start(self):
        self.crawl('https://www.tripadvisor.cn/Attractions-g186338-Activities-London_England.html', callback=self.index_page)

    @config(age=10 * 24 * 60 * 60)
    def index_page(self, response):
        for each in response.doc('#ATTR_ENTRY_ > div.attraction_clarity_cell > div > div > div.listing_info > div.listing_title > a').items():
            self.crawl(each.attr.href, callback=self.detail_page)

        next=response.doc('.pagination  .nav next').attr.href
        self.crawl(next,callback=self.index_page)


    @config(priority=2)
    def detail_page(self, response):#详情页的信息
        url=response.url
        name=response.doc('.heading_title').text()
        comment=response.doc('div > .more').text()
        address=response.doc('.colCnt3').text()
        phone=response.doc('.headerBL > div > .phone').text()

        return {
            "name": name,
            "commment": comment,
            "address":address,
            "phone":phone,
            "url":url,
        }

    def on_result(self,result):#自己定义一个result的方法,用于进行相关的存储
        if result:#如果从上面的detail_page里有相关的输出那么就进行存储
            self.save_to_mongo(result)

    def save_to_mongo(self,result):#将输出结果插入mongodb
        if self.db['london'].insert(result):
            print ('save to mongo success',result)

二.爬取网页时,当大家爬取(table)中的内容时,用xpath解析不到表格中td的值

三. 编写制定主要爬虫

编纂爬虫时可利用暗中认可模板来扭转爬虫文件,命令行命令为scrapy genspider [options] <name> <domain>

# -*- coding: utf-8 -*-
import scrapy
from collectips.items import CollectipsItem
from scrapy.selector import Selector


class XiciSpider(scrapy.Spider):
    name = "xici"   #爬虫名称
    allowed_domains = ["http://www.xicidaili.com"]  #允许爬取的范围

    #重写start_requests()函数,生成多个要爬取的页面
    def start_requests(self):   
        reqs = []
        for i in range(1, 21):
            req = scrapy.Request(
                'http://www.xicidaili.com/nn/{}'.format(i))
            reqs.append(req)

        return reqs

    #编写parse()函数对爬取的网页内容进行分析,提取出想要的部分
    def parse(self, response):
        item = CollectipsItem()
        sel = Selector(response)
        #使用xpath选择器来选取我们想要的网页标签内容
        for i in range(2, 102):
            item['IP'] = sel.xpath(
                '//*[@id="ip_list"]/tr[{}]/td[2]/text()'.format(i)).extract_first()
            item['PORT'] = sel.xpath(
                '//*[@id="ip_list"]/tr[{}]/td[3]/text()'.format(i)).extract_first()
            item['DNS_POSITION'] = sel.xpath(
                '//*[@id="ip_list"]/tr[{}]/td[4]/a/text()'.format(i)).extract_first()
            item['TYPE'] = sel.xpath(
                '//*[@id="ip_list"]/tr[{}]/td[6]/text()'.format(i)).extract_first()
            item['SPEED'] = sel.xpath(
                '//*[@id="ip_list"]/tr[{}]/td[7]/div/@title'.format(i)).extract_first()
            item['LAST_CHECK_TIME'] = sel.xpath(
                '//*[@id="ip_list"]/tr[{}]/td[10]/text()'.format(i)).extract_first()
            yield item

编辑爬虫提取标签时可使用scrapy自带的shell工具举办调和,命令行命令为scrapy shell 'website',若爬取成功,则会回去三个看似ipython的工具,可使用response.headers来查阅再次来到的headers,选用response.xpath('//some/path/title/text()')来调整提取的情节

3.Pyspider 官方document入眼介绍

  • command lines: pyspider -help 来博取其颇具的options
  • 命令行输入pyspider all 运营pyspider后,会获取五个data文件,用于存款和储蓄config file:**Config file is a JSON file with config values for global options or subcommands **
  • webui命令能够用来设定接口登录的密码: --need-auth need username and password /////cmd 端口输入:pyspider --need-auth --username xxx and pasword 1贰③ 可进展接口用户名和密码的设定
  • API reference:
    一 self.crawl方法中的age: the period of validity of the task,指的是发起任务的限时一般以config的方式注明:@config(⑩2460*60)b表示为拾天的限时
    二 self.crawl方法中的priority: the priority of task to be scheduled, higher the better. default: 0在造访多少个url 的时候钦定优先级
    三 self.crawl方法中的exetime: the executed time of task in unix timestamp. default: 0 试行时间依然延迟时间,
    exetime=time.time() 60意味着本地时间60s后实行顺序
    四 self.crawl方法中的exetime:
    self.crawl(item.find('a').attr.url, callback=self.detail_page
    itag=item.find('.update-time').text())
    In the sample, update-time is used as itag. If it's not changed, the request would be discarded.
    5 self.crawl方法中的auto_crawl: when enabled, task would be recrawled every age time
    六self.crawl格局中的parmas:能够将Url的片段参数设定为2个字典的花样
    self.crawl('http://httpbin.org/get', callback=self.callback
    params={'a': 123, 'b': 'c'})
    self.crawl('http://httpbin.org/get?a=123&b=c', callback=self.callback)
    那两者是等价的
    7self.crawl艺术中的allow_redirects:只得是呼吁有个别分界面包车型地铁时候,会高出30②等的提示跳转,那么1旦酱allow_redirects=False的话,就不会促成全自动跳转,该参数default 为True
    8 self.crawl主意中的method: 是用于钦赐请求方法,常见的为post,get 两种请求
    九 self.crawl方法中的fetch_type: set to js to enable JavaScript fetcher
    有时网页是js 渲染过的,举个例子Tmall页面,那么在Pyspider的web可视端,会看不到图片等的剧情,那么将 fetch_type='js'则会解析js 渲染的分界面,默感觉 None
    10 self.crawl方法中的save: a object pass to the callback method, can be visit via response.save
    def on_start(self):
    self.crawl('http://www.example.org/', callback=self.callback
    save={'a': 123})
    def callback(self, response):
    return response.save['a']
    123 would be returned in callback

  原因:浏览器会在table标签下增添tbody(注:在chrome、火狐测试都有这一个处境。出现那种原因是因为浏览器会对html文本进行一定的正规化化 )

四. 防护爬虫被ban的部分方法

  1. 更动设置文件settings.py,禁止cookies
  2. 延伸爬取间隔时间delay
  3. 动用多少个User-Agent
  4. 采替代理ip

三、Scrapy命令详解

运用scrapy 就须要在cmd中去做到部分下令,那几个命令会帮忙组建文件,存款和储蓄scrapy 自带的各种区块文本,接下去先介绍其关键命令:
实际情况请看document:http://scrapy-chs.readthedocs.io/zh_CN/0.24/(中文版)

  • scrapy startproject myproject
  • scrapy genspider zhihu https://www.zhihu.com/(以果壳网为例)
  • scrapy genspider -l 能够行使提前定义好的沙盘来生成spider
  • scrapy crawl zhihu.py 运营爬虫程序
  • scrapy check -l 是用来检查写的spider是或不是有荒唐
  • scrapy fetch https://www.zhihu.com/ 用来输出所要爬取的链接
    HTML解析内容,在剧情前边会有为数不少日记一齐输出。也足以加入变量
    --no log 来防止前面日志的出口:scrapy fetch --nolog https://www.zhihu.com/
  • scrapy view https://www.zhihu.com/,那几个命令也是相比较好用,能够在浏览器里活动张开搜狐分界面,可用于互动调节和测试
    -scrapy shell https://www.zhihu.com/,就能够在指令窗口跳转到python编辑器,变成相互格局,在这边能够输入 response,则会输出https://www.zhihu.com/,输入response.text,则会输出该网页的html解析文本内容,输入view,则会回来 True,并且会打开相应的浏览分界面,类似的还有response.headers、response.css(.......).extract_first()等。
  • scrapy parse --callback method(个中措施正是多少个scrapy框架在那之中的任意的法子名称,比方Item.Py 中的item 方法
  • scrapy settings --get name ,是用来获得setting.py文本中设定的消息,比方cookies,MONGO_URI,MONGO_DB等,以MOGO_URI为例,scrapy settings --get MONGO_U帕杰罗I,输出应为localhost
  • scrapy version 输出scrapy 版本,scrapy version --v 会同时输出其安装信赖库的版本
  • scrapy bench 输出 运维benchmark 后的局地参数,举例运转的快慢

  化解措施:消除措施:使用scrapy 时将分析出的xpath 中的 tbody 去掉就可以。

5. 使用chrome插件xpath helper十分的快获得目标内容的竹签

行使xpath方法获得目标内容标签时,有一点地点浏览器会对源html进行退换,加入贰个tbody标签,假设大家利用的xpath标签中包蕴tbody,将不能赢得科学内容,必要手动删除tbody这几个标签.
xpath helper能援助大家非常的慢获得目标内容的标签,方便提取.使用方法为:张开目标网页后,Ctrl shift X调出xpath helper的调整台,将鼠标移至其余目标内容地点,按下shift键就可以在调节台中查看鼠标所在内容的标签以及其内容.

四、Scrapy 语法详解

首先是selector的用法 Scrapy 提取数额有和好的1套机制,通过特定的xpath 也许css 选拔器来选用HTML中的一些片段,接下去大家运用官方文书档案中的HTML code 来教学一下selector 的用法

<html>
 <head>
  <base href='http://example.com/' />
  <title>Example website</title>
 </head>
 <body>
  <div id='images'>
   <a href='image1.html'>Name: My image 1 <br /><img src='image1_thumb.jpg' /></a>
   <a href='image2.html'>Name: My image 2 <br /><img src='image2_thumb.jpg' /></a>
   <a href='image3.html'>Name: My image 3 <br /><img src='image3_thumb.jpg' /></a>
   <a href='image4.html'>Name: My image 4 <br /><img src='image4_thumb.jpg' /></a>
   <a href='image5.html'>Name: My image 5 <br /><img src='image5_thumb.jpg' /></a>
  </div>
 </body>
</html>

Scrapy 其自个儿自带二种选拔器,response.xpath 和response.css

response.selector.xpath('//title/text()') []

#注意返回的类型为选择器,想要获取其内部的文本内容,并以列表的形式输出,就要用到extract()
#同样的response.selector.css('title::text').extract()也可以达到上述同样的效果
#如果想要获得第一个url 及参数href 对应的内容,可以用以下两种写法:
>>> response.xpath('//base/@href').extract()
>>> response.css('base::attr(href)').extract()

#想要获取a 标签里的参数href的内容,可以有以下方法:
>>> response.xpath('//a[contains(@href, "image")]/@href').extract()
[u'image1.html',
 u'image2.html',
 u'image3.html',
 u'image4.html',
 u'image5.html']
>>> response.css('a[href*=image]::attr(href)').extract()
[u'image1.html',
 u'image2.html',
 u'image3.html',
 u'image4.html',
 u'image5.html']
或者用response.xpath('//div[@id="image"]).css('a'::attr(href)).extract_first(default=" ")这个方法是将两个选择器结合使用,请用心体会
#contains的作用是指href 参数内容当中包含imag字段,以防有多个href 参数值

#如果想要获取a标签里的img 标签的src 属性怎么办?可以用以下三种方法:
>>> response.xpath('//a[contains(@href, "image")]/img/@src').extract()
[u'image1_thumb.jpg',
 u'image2_thumb.jpg',
 u'image3_thumb.jpg',
 u'image4_thumb.jpg',
 u'image5_thumb.jpg']
>>> response.css('a[href*=image] img::attr(src)').extract()
[u'image1_thumb.jpg',
 u'image2_thumb.jpg',
 u'image3_thumb.jpg',
 u'image4_thumb.jpg',
 u'image5_thumb.jpg']

或者:response.xpath('//div[@id="image"]).css('img'::attr('src')).extract_first()

#如何查找a标签里的文本内容
当然可以用response.css('a::text').extract_first()
返回结果是Name:My image 1,

那么哪些将前方的Name:去掉啊?别着急,selector 还提供正则匹配

response.css('a::text').re('Name:(.*?)')
#返回结果是所有匹配内容
#同样的re 与extract一样,有re_extract(),方法得到第一个
response.css('a::text').re_first('Name:(.*?)')

  参考自:

伍、Scrapy 的框架分解

spider 类的使用

  • name 是唯一标记该spider 的习性
  • allowed_domains 是保障其域名与url相互1致的品质
  • start_urls 存款和储蓄要进行访问的链接,是二个列表,能够储存多个列表
  • start_requests 该方法必须重临二个可迭代对象(iterable),一般用yield构造。该目的涵盖了spider用于爬取的首先个Request
示例:yield Scarapy.Request(url,method=post or get ,callback=)
  • make_requests_from_url 其本质上是和start_requests并列,效果都以回到请求request的答复(response),借使双方共存,则只回去strat_requests的伏乞结果,其两端的个中关系代码如下:
def strat_requests(Self):
           for url in start_urls:
                    yield make_from_url(url)
def make_from_url(self):
          return Request(url,dont_filter=True)
  • parse 方法,正是用来进展主页大概详细的情况页爬取新闻的艺术
  • log 方法,可以输出模块logging,利用内部的logger方法来实行输出调试音讯用于调节和测试(详细请看Scrapy实例演示)

itempipline 类的用法
内部优良的正是MongoDB的蕴藏实例

import pymongo

class MongoPipeline(object):

    collection_name = 'user'

    def __init__(self, mongo_uri, mongo_db):
        self.mongo_uri = mongo_uri
        self.mongo_db = mongo_db

    @classmethod
    def from_crawler(cls, crawler):
        return cls(
            mongo_uri=crawler.settings.get('MONGO_URI'),
            mongo_db=crawler.settings.get('MONGO_DATABASE', 'items')
        )

    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.collection_name].update({'url_token':item['url_token']},{'$set':item},True)#查找到就更新,没有就插入
        return item
  • from_crawler(cls,crawler) 方法实质就是将概念在settlings.py文本里的七个变量('MONGO_URI','MONGO_DATABASE')引用过来
  • open_spider 即是用来开启存储程序
  • close_spider正是用来关闭MongoDB
  • process_item (self,item,spider)方法正是将新闻插入MongoDB或然更新新闻

叁.scrapy仿照登入博客园时,输入验证码后出现“缺乏验证码票据”,在settings.py将cookie设置为如下:

陆、Scrapy 的框架爬取的实例演示

一.爬取果壳网某1个用户关切者,和关切的人的新闻,以及层层嵌套音信(着重在于理解框架的运用和布局以及迭代巡回)
2.爬取乐乎实战(入眼在于headers、cookies的结构)

COOKIES_ENABLED = True

1.爬取果壳网音讯

class ZhihuSpider(Spider):
    name = 'zhihu'
    allowed_domains = ['www.zhihu.com']
    start_urls = ['http://www.zhihu.com/']
def parse(self,response):
     pass

启用初叶程序会500状态码,那是因为乐乎的反爬系统
此刻就必要在settings结构里去设定

# Obey robots.txt rules
ROBOTSTXT_OBEY = False
DEFAULT_REQUEST_HEADERS = {
   'Accept': 'text/html,application/xhtml xml,application/xml;q=0.9,*/*;q=0.8',
   'Accept-Language': 'en',
   'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.167 Safari/537.36',(来自知乎某位用户的网页XHR-Response)
   'authorization': 'Bearer 2|1:0|10:1521895735|4:z_c0|92:Mi4xOWRIc0JRQUFBQUFBb09EV3hSWldEU1lBQUFCZ0FsVk5ONWVqV3dDMkFXT2s0SUFWT182RS1WTGhlQWI2UFZkTVZ3|a40f82d336aa3c0f7cac35e8201d6bbd1f397d22bdbc5c46157a61182b05bf30'
}(来自知乎的某位用户网页XHR-Response)
![image.png](https://upload-images.jianshu.io/upload_images/10516872-4d8c88d3e54dad5f.png?imageMogr2/auto-orient/strip|imageView2/2/w/1240)

某一个人用户的主页面包车型客车网站:

https://www.zhihu.com/api/v4/members/jinrongbaguanv?include=allow_message,is_followed,is_following,is_org,is_blocking,employments,answer_count,follower_count,articles_count,gender,badge[?(type=best_answerer)].topics

能够看来它的结构由url base 和include 两某个组成

该位用户‘关心了’的主页面的网站如下:

https://www.zhihu.com/api/v4/members/jinrongbaguanv/followees?include=data[*].answer_count,articles_count,gender,follower_count,is_followed,is_following,badge[?(type=best_answerer)].topics&offset=0&limit=20

能够看看该网站首要由3片段组成:第1片段为url base 第3部分为offset(用于注明第几页,0为第壹页,20为第3页,就那样推算),第一有的为limit(即表示每1页展现该用户关切了的人(followees的个数),经过如上分析就能够用start_request(self)方法,来组织动态url

class ZhihuSpider(Spider):
    name = 'zhihu'
    allowed_domains = ['www.zhihu.com']
    start_urls = ['http://www.zhihu.com/']
    start_user='jinrongbaguanv'
    user_url='https://www.zhihu.com/api/v4/members/{user}?include={include}'#用户的详情页的url
    user_query='allow_message,is_followed,is_following,is_org,is_blocking,employments,answer_count,follower_count,articles_count,gender,badge[?(type=best_answerer)].topics'
follows_url='https://www.zhihu.com/api/v4/members/{user}/followees?include={include}&offset={offset}&limit={limit}'
 follows_query='data[*].answer_count,articles_count,gender,follower_count,is_followed,is_following,badge[?(type=best_answerer)].topics'
 def start_request(self):
        yield Request(self.user_url.format(user=self.start_user,include=self.user_query),callback=self.parse_user)
        yield Request(self.follows_url.format(user=self.start_user,include=self.follows_query,offset=0,limit=20),callback=self.parse_follows)

上面首先分析该用户主页的音讯,即选拔parse_user(srlf,response)来提取该用户的中坚新闻:

  • 率先就要要提取的新闻放到items.py文件里,举办宣示
import scrapy
from Scrapy import Item
from Scrapy import Field


class UserItem(Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    id=Field()
    answer_count=Field()
    articles_count=Field()
    name=Field()
    url=Field()
    url_token=Field()
    user_type=Field()

随之在zhihu.py主程序里编写parse_user(srlf,response)方法:

    def parse_user(self, response):#抓取的目标用户的信息内容
        result=json.loads(response.text)#对输出的json数据行一个加载
        item=UserItem()
        for field in item.fields:#items.fields方法会返回一个以Items.py中声明的所有要提取的信息的名字的集合
            if field in result.keys():
                item[field]=result.get(field)#就获取值
        yield item #成功获取item

接下去进入该用户的'关心了'页面里面,会意识它的XH牧马人-preview里存放了每一页关切她的人的基本新闻,由此得以接纳parse_follows(self,response)来将该用户关注了的人的主干新闻爬抽出来

 def parse_follows(self, response):
        results=json.loads(response.text)#先解析为json 格式
        if 'data' in results.keys():
            for result in results.get('data'):
                yield Request(self.user_url.format(user=result.get('url_token'),include=self.user_query),callback=self.parse_user)#拿到该用户所关注的人的详情页的url,以便于后面的层层嵌套迭代,并仍用parse_user方法进行详情页解析
        if 'paging' in results.keys() and results.get('paging').get('is_end')==False:#判断pagging 是否在这个返回的json 里,且是否到达最后一页
            next_page=results.get('paging').get('next')#拿到该用户关注的人的下一页的链接,实际就是翻页
            yield Request(next_page,calback=self.parse_follows)#调用自己实现循环

那就是说怎么样兑现该用户关注了的人关注的人(很别扭对吧,笔者一度努力了)的新闻呢,其实本质正是二层爬取,那当然是在parse_user(self,response)里面完结

yield Request(self.follows_url.format(user=result.get('url_token'),include=self.follows_query,offset=0,limit=20),callback=self.parse_follows)#关注用户自己的关注用户,层层套用

即上述的parse_user(self,response)程序修改如下:

 def parse_user(self, response):#抓取的目标用户的信息内容
        result=json.loads(response.text)#对输出的json数据行一个加载
        item=UserItem()
        for field in item.fields:#items.fields方法会返回一个以Items.py中声明的所有要提取的信息的名字的集合
            if field in result.keys():
                item[field]=result.get(field)#就获取值
        yield item #成功获取item
        yield Request(self.follows_url.format(user=result.get('url_token'),include=self.follows_query,offset=0,limit=20),callback=self.parse_follows)#关注用户自己的关注用户,层层套用
        yield Request(self.followers_url.format(user=result.get('url_token'),include=self.followers_query,offset=0,limit=20),callback=self.parse_followers)

除了那么些之外能够得到该用户关注的人的消息,当然你也得以赢得关心该用户的人的新闻:
网页分析如下:

![image.png](https://upload-images.jianshu.io/upload_images/10516872-a99460304ee056c1.png?imageMogr2/auto-orient/strip|imageView2/2/w/1240)

依附完整的主程序:

import scrapy
from scrapy import Spider
from scrapy import Request
import json 
from zhuhuuser.items import UserItem

class ZhihuSpider(Spider):
    name = 'zhihu'
    allowed_domains = ['www.zhihu.com']
    start_urls = ['http://www.zhihu.com/']
    start_user='jinrongbaguanv'
    user_url='https://www.zhihu.com/api/v4/members/{user}?include={include}'#用户的详情页的url
    user_query='allow_message,is_followed,is_following,is_org,is_blocking,employments,answer_count,follower_count,articles_count,gender,badge[?(type=best_answerer)].topics'
    follows_url='https://www.zhihu.com/api/v4/members/{user}/followees?include={include}&offset={offset}&limit={limit}'#每一页关注了的url
    follows_query='data[*].answer_count,articles_count,gender,follower_count,is_followed,is_following,badge[?(type=best_answerer)].topics'

    followers_url='https://www.zhihu.com/api/v4/members/{user}/followers?include={include}&offset={offset}&limit={limit}'#关注者的Url
    followers_query='data[*].answer_count,articles_count,gender,follower_count,is_followed,is_following,badge[?(type=best_answerer)].topics'#关注者的query

    def start_request(self):
        yield Request(self.user_url.format(user=self.start_user,include=self.user_query),callback=self.parse_user)
        yield Request(self.follows_url.format(user=self.start_user,include=self.follows_query,offset=0,limit=20),callback=self.parse_follows)
        yield Request(self.followers_url.format(user=self.start_user,include=self.followers_query,offset=0,limit=20),callback=self.parse_followers)


    def parse_user(self, response):#抓取的目标用户的信息内容
        result=json.loads(response.text)#对输出的json数据行一个加载
        item=UserItem()
        for field in item.fields:#items.fields方法会返回一个以Items.py中声明的所有要提取的信息的名字的集合
            if field in result.keys():
                item[field]=result.get(field)#就获取值
        yield item #成功获取item
        yield Request(self.follows_url.format(user=result.get('url_token'),include=self.follows_query,offset=0,limit=20),callback=self.parse_follows)#关注用户自己的关注用户,层层套用
        yield Request(self.followers_url.format(user=result.get('url_token'),include=self.followers_query,offset=0,limit=20),callback=self.parse_followers)

    def parse_follows(self, response):
        results=json.loads(response.text)#先解析为json 格式
        if 'data' in results.keys():
            for result in results.get('data'):
                yield Request(self.user_url.format(user=result.get('url_token'),include=self.user_query),callback=self.parse_user)#拿到该用户所关注的人的详情页的url,以便于后面的层层嵌套迭代,并仍用parse_user方法进行详情页解析
        if 'paging' in results.keys() and results.get('paging').get('is_end')==False:#判断pagging 是否在这个返回的json 里,且是否到达最后一页
            next_page=results.get('paging').get('next')#拿到该用户关注的人的下一页的链接,实际就是翻页
            yield Request(next_page,calback=self.parse_follows)#调用自己实现循环

    def parse_followers(self, response):
        results=json.loads(response.text)#先解析为json 格式
        if 'data' in results.keys():
            for result in results.get('data'):
                yield Request(self.user_url.format(user=result.get('url_token'),include=self.user_query),callback=self.parse_user)
        if 'paging' in results.keys() and results.get('paging').get('is_end')==False:#判断pagging 是否在这个返回的json 里
            next_page=results.get('paging').get('next')#拿到下一页的链接
            yield Request(next_page,calback=self.parse_followers)#调用自己实现循环

终极正是在itempipline中投入存款和储蓄到MongoDB的主次

import pymongo

class MongoPipeline(object):

    collection_name = 'user'

    def __init__(self, mongo_uri, mongo_db):
        self.mongo_uri = mongo_uri
        self.mongo_db = mongo_db

    @classmethod
    def from_crawler(cls, crawler):
        return cls(
            mongo_uri=crawler.settings.get('MONGO_URI'),
            mongo_db=crawler.settings.get('MONGO_DATABASE', 'items')
        )

    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.collection_name].update({'url_token':item['url_token']},{'$set':item},True)#True查找到就更新,没有就插入,而第一个参数表示进行查重的标识符
        return item

自然你还亟需在setting.py文本中宣称:

![image.png](https://upload-images.jianshu.io/upload_images/10516872-f9e9ae20243f4ff8.png?imageMogr2/auto-orient/strip|imageView2/2/w/1240)

2. 博客园爬取

乐乎爬取的主程序未有何尤其的,首要讲一下,cookies配置(middle ware.py里安顿)和pipline.py 中对领取音讯的二个革新
middleware.py里的cookies 配置如下:

import requests
import json
from requests.exceptions import ConnectionError 
import logging #输出日志



class CookiesMiddleware():
    def __init__(self,cookies_pool_url):
        self.logger=logging.getLogger(__name__)
        self.cookies_pool_url=cookies_pool_url#获取一个cookies,前提是已经建立了自己的cookies池

    def get_random_cookies(self,request,spider):
        try:
            response=requests.get(self.cookies_pool_url)
            if response.status_code==200:
                json.loads(response.text)#如果成功进入端口,那么将内容用json 解析出来
        except ConnectionError:
            return None 
    def from_crawler(cls,crawler):#该函数用于从setting 中调取一个cookies_pool_url
        return cls(cookies_pool_url=crawler.setting.get('COOKIES_POOL_URL'))

    def process_request(self,request,spider):
        cookies=self.get_random_cookies()#调用方法对象,获取cookies
        if cookies:#判断为真
            request.cookies=cookies#请求获得一个cookies
            self.logger.debug('using cookies' json.dumps(cookies))#dumps 是将json格式的转化为str,关于dumps和dump,loads 可看:https://www.cnblogs.com/wswang/p/5411826.html
        else:
            self.logger.debug('no valid cookies')

    def process_response(self,request,response,spider):#因为微博的反爬比较厉害,所以加入这个参数进行判断


            if response.status in [300,301,302,303]:
                try:
                    redirect_url=response.headers['location']
                    if 'passport' in redirect_url:
                        self.logger.debug('need login updating new cookies')#用logging的logger属性来打印日志,进行调试
                    elif 'weibo.cn/security' in redirect_url:
                            self.logger.debug('account is locked')
                        request.cookies=self.get_random_cookies()
                    return request
                except:
                    raise IngoreRequest
                elif response.status in [414]:
                    return request#只需重新请求即可
                else:
                    return response#其他情况正常的输出响应内容即可

pipline.py 文本中得以编写对item音讯进行改进的代码:

import re
import time
import pymongo

class WeibosearchPipeline(object):#改写的作用主要是针对时间posted_time,因为他会输出今天,明天,不是具体的日期,想把它改为几几年几月几日
    def parse_time(self,datetime):#一般微博上显示时间有三种:1,几分钟前,2:今天 几点,3:几月几号没有年份
        if re.match('d 月d 日',datetime):
            datetime=time.strftime('%Y',time.localtime()) '年' datetime
        if re.match('d 分钟前',datetime):
            minutes=re.match('(d )',datetime).group(1)#拿出分钟数
            datetime=time.strftime('%Y%m%d %H:%M',time.localtime(time.time()-float(minutes)*60))#time.time 输出的是多少秒
        if re.match('今天.*',datetime):
            datetime=re.match('今天(.*?')',datetime).group(1).strip()
            datetime=time.strftime('%Y%m%d %H:%M',time.localtime()) datetime


    def process_item(self, item, spider):
        if isinstance(item,WeiboItem):#判断item来自weiboitem
            if item.get('content'):
                item['content']=item['content'].lstrip(':').strip()
            if item.get('posted_time'):
                item['posted_time']=item['posted_time'].strip()
                item['posted_time']=self.parse_time(item['posted_time'])#对时间进行转义
        return item


class MongoPipline():
    def __init__(self,mongo_uri,mongo_db):
        self.mongo_uri=mongo_uri
        self.mongo_db=mongo_db
    def from_crawler(cls,crawler):
        return cls(mongo_uri=crawler.settings.get('MONGO_URI'),MONGO_DB=crawler.settings.get('MONGO_DB'))
    def open_spider(self,spider):
        self.client=pymongo.MongoClient(self.mmongo_uri)
        self.db=self.client[self.mongo_db]

    def close_spider(self,spider):
        self.client.close()
    def process_item(self,item,spider):
        self.db[item.table_time].update({'id':item.get('id')},{'$set':dict(item)},True)
        return item 

七、总结:

  • 多去实践精晓相比较五个框架的分歧之处
  • 要去通晓Scrapy框架的总体结构完整性,和每2个文件的成效,全体的逻辑性
  • 上述总计来源于官方文书档案 崔庆才--爬虫录像 本身明白
  • 记忆犹新,必有回音

本文由必发88手机版发布,转载请注明来源:爬虫入门,scrapy爬虫踩的坑