python爬虫学习笔记-scrapy框架之start_url

在使用命令行创建scrapy项目后,会发现在spider.py文件内会生成这样的代码:

name = 'quotes'
allowed_domains = ['quotes.toscrape.com']
start_urls = ['http://quotes.toscrape.com/']

其中比较好理解的是name,这个字段代表爬虫项目名称,在命令行创建时已经指定,allowed_domains代表允许爬取的域名,这两个字段均在创建时已经设置好,不需要更改,但比较特殊的是start_url这个字段,它也是一个列表形式,官方文件的解释为包含spider在启动时爬取的url列表,用于定义初始请求。这样就可以类比如果不使用scrapy框架,url应该如何定义;
例如爬取豆瓣读书时发现首页url为"https://book.douban.com/top250?start=0",
以后每翻页一次start字段值递增25,这样就可以很容易用一个循环做到url的构建,再比如如果爬取今日头条图片,分析Ajax后首页url为“https://www.toutiao.com/search_content/?offset=0&format=json&keyword=街拍&autoload=true&count=20&cur_tab=1&from=search_tab&pd=synthesis”,以后每页更改其中offset参数递增20,则可以利用urlencode()方法构建url。
其实scrapy的start_url构建方法与普通爬虫的构建本质上没有任何区别,只是因为在框架的调度使其实现更加容易。
下面以http://images.so.com网站为例:
创建好项目文件后,spider.py内的代码如下:

class ImagesSpider(Spider):
    name = 'images'
    allowed_domains = ['images.so.com']
    start_urls = ['http://images.so.com/']

     def prase(self):
     	pass

如果我们不做任何其他处理,则爬虫启动只会爬取"http://images.so.com/"上的内容。
因为这是一个列表形式,笨一点的方法就是把所有要爬取的url添加到列表中,然后遍历就可以了:

start_urls = [
           'http://images.so.com/z?ch=beauty',
           'http://images.so.com/z?ch=wallpaper']

       for url in start_urls:
           yield scrapy.Request(url=url, callback=self.parse)

这样当然没错,而且还很好理解,但未免显得太实在了。既然要返回一组列表,自然而然就可以用到以前的构建方法,构建代码如下:

    def start_requests(self):
        data={'ch':'photography','listtype':'new'}
        base_url='https://image.so.com/zj?'
        for page in range(1,self.settings.get('MAX_PAGE')+1):
            data['sn']=page*30
            params=urlencode(data)
            url=base_url+params
            yield Request(url=url,callback=self.parse)

这里的url是分析Ajax得到的,可以看出构建方法没有什么特别之处,这个方法返回的Request请求,而回调函数就是我们解析html的方法,爬虫运行时,不断的将请求生成并被parse()方法调用解析,parse()方法代码如下:

    def parse(self, response):
        result=json.load(response.text)
        for image in result.get('list'):
            item=Images360Item()
            item['id']=image.get('imageid')
            item['url']=image.get('qhimg_url')
            item['title']=image.get('group_title')
            item['thumb']=image.get('qhimg_thumb_url')
            yield item

当涉及到不止一层关系的爬虫,例如微博的爬取,爬取了微博首页后,还要爬取它的关注,粉丝,微博列表的情况下,就相对复杂的多了。但其实本质是一样的,只是下一层的start_url的构建不是在start_request()方法中,而是在第一层罗辑的解析方法中生成,伪代码如下,以微博为例:

class WeibocSpider(Spider):
    name = 'weiboc'
    allowed_domains = ['m.weibo.cn']
    user_url=''
    follow_url=''
    fan_url=''
    weibo_url=''
    start_user=['3217179555']
    def start_requests(self):
        for uid in self.start_user:
            yield Request(self.user_url.format(id=uid),callback=self.parse_user)

    def parse_user(self,response):
        self.logger.debug(response)
        result=json.loads(response.text)
        if result.get('data').get('userInfo'):
            user_Info=result.get('data').get('userInfo')
            user_item=UserItem()
            field_map={}
            for field ,attr in field_map.items():
                user_item[field]=user_Info.get['attr']#返回用户信息


            #关注
            uid=user_Info.get('id')
            #构造下一页链接,并返回Request
            yield Request(self.follow_url.format(uid=uid,page=1),callback=self.parse_follows,meta={'page':1,'uid':uid})

            #fans
            yield Request(self.fan_url.format(uid=uid,page=1),callback=self.parse_fans,meta={'page':1,'uid':uid})

            #weibo
            yeild Request(self.weibo_url.format(uid=uid,page=1),callback=self.parse_weibos,meta={'page':1,'uid':uid})





    def parse_follows(self,response):
        """
        解析用户关注
        :param response: Response对象
        :return:
        """
        result=json.loads(response.text)
        if result.get('ok') and result.get('data').get('cards') and len(result.get('data').get('cards'))
            and result.get('data').get('cards')[-1].get('card_group'):
            #解析关注列表每个用户信息并发起新的解析请求
            follows=result.get('data').get('cards')[-1].get('card_group')
            for folow in follows:
                if follow.get('user'):
                    uid=follow.get('user').get('id')
                    yield Request(self.user_url.format(uid=uid)callback=self.parse_user())

            #关注列表
            uid=response.meta.get('uid')
            user_relation_item=UserRelationItem()
            follows=[{'id':follow.get('user').get('id'),'name':follow.get('user').get('screen_name')}for follow in follows]
            user_relation_item['id']=uid
            user_relation_item['follows']=follows
            user_relation_item['fans']=[]
            yield  user_relation_item

            #提取下一页的关注
            page=response.meta.get('page')+1
            yield  Request(self.follow_url.format(uid=uid,page=page),callback=self.parse_follows,meta={'page':page.'uid':uid})

这里首先通过start_request()方法生成要爬取的微博首页请求,在parse_users()方法解析得到用户信息后,再生成下一层罗辑需要的请求列表,这样就可以实现一层层不断的爬取,貌似用到了深度优先的概念。
ps:GitHub地址:https://github.com/linl12138/image

猜你喜欢

转载自blog.csdn.net/weixin_42672765/article/details/85380212