• 爬虫--scrapy


    电影天堂爬虫实例

    将要通过代码实现的是:找到其中是最新电影并且评分高于8.0的电影。

    第一步代码实现:

     1 # _*_ coding:utf-8 _*_
     2 # _author:khal_Cgg
     4 import requests
     5 from bs4 import BeautifulSoup
     6 import re
     7 req = {}                # 最后返回的电影列表
     8 def re_func(text):      # 每一次请求都会被这个函数处理分析是否过标准
     9     global req
    10     soup = BeautifulSoup(text,"lxml")     # 创建beautifulsoup
    11     table_list = soup.find_all(name="table",attrs={"class":"tbspan"}) # 找到每一个电影的table
    12     for one_table in table_list:           #  循环每一个table
    13         soup2 = BeautifulSoup(str(one_table),"lxml")
    14         first_a = soup2.find(name="a")     # 找到的第一个a标签就是电影分类
    15         if first_a.string == "[最新电影]":  # 若是最新电影则再判断其他条件
    16             film_name = soup2.find_all(name="a")[1]
    17             td = soup2.find_all(name="td")[5]
    18             re_aim = td.string
    19             gaga = re.findall("豆瓣评分 (d{1}.d{1})|IMDb评分 (d{1}.d{1})",re_aim) #找评分
    20             if gaga:           # 判断评分是否满足一定的要求
    21                 douban = gaga[0][0]
    22                 if douban:
    23                     douban = float(douban)
    24                     if douban >= 8.0:
    25                         req[film_name.string] = {}
    26                         req[film_name.string]["豆瓣得分"] = douban   #  若是满足就写入列表
    27                 else:
    28                     imdb = gaga[0][1]
    29                     if imdb:
    30                         imdb = float(imdb)
    31                         if imdb >= 8.0:
    32                             req[film_name.string]={}
    33                             req[film_name.string]["IMDb得分"] = imdb
    34     return req
    35 base_url = "http://www.ygdy8.net/html/gndy/china/list_4_%s.html"
    36 for i in range(1,20):            # 循环生成url就行requests
    37     print('++++++++++',i)
    38     user = base_url%(i)
    39     print(user)
    40     r1 = requests.get(user)
    41     r1.encoding="gbk"
    42     re_func(r1.text)
    43 print(req)

    上述代码就是只用requests模块最简单的方式进行爬虫。

    缺点

    • 手动查找页码的url,还好电影天堂是页码url固定,不然就得一个一个页面的访问。
    • 代码是阻塞同步的,遇到网络卡顿直接down无结果。

    第二步gevent异步优化

    # _*_ coding:utf-8 _*_
    import requests
    from bs4 import BeautifulSoup
    import re,gevent
    req = {}
    class Singleton(object):
        def __new__(cls, *args, **kwargs):
            if not hasattr(cls,'_instance'):
                orig = super(Singleton,cls)
                cls._instance =  orig.__new__(cls)
            return cls._instance
    class re_func(Singleton):
        def __init__(self,text):
            global req
            soup = BeautifulSoup(text,"lxml")
            table_list = soup.find_all(name="table",attrs={"class":"tbspan"})
            for one_table in table_list:
                soup2 = BeautifulSoup(str(one_table),"lxml")
                first_a = soup2.find(name="a")
                if first_a.string == "[最新电影]":
                    film_name = soup2.find_all(name="a")[1]
                    td = soup2.find_all(name="td")[5]
                    re_aim = td.string
                    gaga = re.findall("豆瓣评分 (d{1}.d{1})|IMDb评分 (d{1}.d{1})",re_aim)
                    if gaga:
                        douban = gaga[0][0]
                        if douban:
                            douban = float(douban)
                            if douban >= 8.0:
                                req[film_name.string] = {}
                                req[film_name.string]["豆瓣得分"] = douban
                        else:
                            imdb = gaga[0][1]
                            if imdb:
                                imdb = float(imdb)
                                if imdb >= 8.0:
                                    req[film_name.string]={}
                                    req[film_name.string]["IMDb得分"] = imdb
    base_url = "http://www.ygdy8.net/html/gndy/china/list_4_%s.html"
    def start(i):
        print('++++++++++',i)
        user = base_url%(i)
        print(user)
        r1 = requests.get(user)
        r1.encoding="gbk"
        re_func(r1.text)
    threads = [gevent.spawn(start, i) for i in range(1,88)]
    gevent.joinall(threads)
    print(req)

    优点:

    • 面向对象单例模式
    • gevent异步优化

    结果:显示中国电影依旧那么烂

    scrapy爬虫框架

     下载安装好后,选择一个目录在命令行中:

    1. scrapy startproject myspider  # 创建一个新的project
    2. cd mtspider  
    3. scrapy henspider dytt yddy8.net  # 创建一个爬虫 名字 和 域名
    4. scrapy crawl dytt --nolog   # 进行爬虫不用日志记录

    settings参数:

    ROBOTSTXT_OBEY = False 爬虫失败改为假

    DEPTH_LIMIT = 2  允许迭代深度2 只迭代一次

     代码:

    # -*- coding: utf-8 -*-
    import scrapy
    from scrapy.http import Request
    import re
    from bs4 import BeautifulSoup
    
    
    class DyttSpider(scrapy.Spider):
        name = "dytt"
        allowed_domains = ["ygdy8.net"]
        start_urls = ['http://www.ygdy8.net/html/gndy/china/index.html']
        visited_set = set()
        req = {}
        encoding = 'gbk'
    
        def take_page(self,page_list):
            req = []
            for biaoqian in page_list:
                href = biaoqian.attrs["href"]
                new_rul = self.start_urls[0].replace("list_4_0.html/",href,1)
                req.append(new_rul)
            print(req)
            return req
    
    
    
        def re_func(self,text):  # 每一次请求都会被这个函数处理分析是否过标准
            soup = BeautifulSoup(text, "lxml")  # 创建beautifulsoup
            # print(text)
            div_x = soup.find_all(name="div",attrs={"class","x"})[1]
            soupX = BeautifulSoup(str(div_x), "lxml")
            page_list = soupX.find_all(name="a",limit=6)
            url_loop = self.take_page(page_list)
            table_list = soup.find_all(name="table", attrs={"class": "tbspan"})  # 找到每一个电影的table
            for one_table in table_list:  # 循环每一个table
                soup2 = BeautifulSoup(str(one_table), "lxml")
                first_a = soup2.find(name="a")  # 找到的第一个a标签就是电影分类
                if first_a.string == "[最新电影]":  # 若是最新电影则再判断其他条件
                    film_name = soup2.find_all(name="a")[1]
                    td = soup2.find_all(name="td")[5]
                    re_aim = td.string
                    gaga = re.findall("豆瓣评分 (d{1}.d{1})|IMDb评分 (d{1}.d{1})", re_aim)  # 找评分
                    if gaga:  # 判断评分是否满足一定的要求
                        douban = gaga[0][0]
                        if douban:
                            douban = float(douban)
                            if douban >= 8.0:
                                self.req[film_name.string] = {}
                                self.req[film_name.string]["豆瓣得分"] = douban  # 若是满足就写入列表
                                print(self.req)
                        else:
                            imdb = gaga[0][1]
                            if imdb:
                                imdb = float(imdb)
                                if imdb >= 8.0:
                                    self.req[film_name.string] = {}
                                    self.req[film_name.string]["IMDb得分"] = imdb
                                    print(self.req)
            return url_loop
        def parse(self, response):
            print(response.url)
            self.visited_set.add(response.url)
            print(response.body.decode('gbk'))
            page_list = self.re_func(response)
            for url in page_list:
                if url in self.visited_set:
                    pass
                else:
                    obj = Request(url=url, method='GET', callback=self.parse,encoding='gbk')
                    yield obj
    View Code

    代码中存在的编码问题没有解决

    例子:

    # -*- coding: utf-8 -*-
    import scrapy
    from scrapy.selector import HtmlXPathSelector
    from scrapy.http import Request
    count = 0
    class XiaohuarSpider(scrapy.Spider):
        name = "xiaohuar"
        allowed_domains = ["xiaohuar.com"]
        start_urls = ['http://www.xiaohuar.com/list-1-0.html']
    
        visited_set = set()
    
        def parse(self, response):
            self.visited_set.add(response.url)
            # 1. 当前页面的所有校花爬下来
            # 获取div并且属性为 class=item masonry_brick
            hxs = HtmlXPathSelector(response)
            item_list = hxs.select('//div[@class="item masonry_brick"]')
            for item in item_list:
                v = item.select('.//span[@class="price"]/text()').extract_first()
                print(v)
                global count
                count +=1
                print(count)
            # 2. 在当前页中获取 http://www.xiaohuar.com/list-1-d+.html,
            # page_list = hxs.select('//a[@href="http://www.xiaohuar.com/list-1-1.html"]')
            page_list = hxs.select('//a[re:test(@href,"http://www.xiaohuar.com/list-1-d+.html")]/@href').extract()
            for url in page_list:
                if url in self.visited_set:
                    pass
                else:
                    obj = Request(url=url,method='GET',callback=self.parse)
                    print(self.parse)
                    return obj   # return 是结束这个函数后再调用 (循环只有一次)所以limit会对其有影响。但是yield是函数暂时停止在这里
                    # 再调用这个函数的时候继续循环第一个函数的for循环 所以这是会先循环第一页上的所有页码1-16。此时第一次调用才结束。所以limit3正好循环完42页。

    通过这里来了解return和yield的差别和depth_limit的作用:

    通过实验:limit在大于等于3的时候yield能循环完成42页的任务。但是return只能完成limit的数值的页数。

    得出结论:limit监控的是parse函数自己被调用的次数return因为是结束了函数所以在循环第二次的时候就是再次调用了函数。

    所以只能爬到limit数值限制的页码。没有limit的时候也能完全爬完所有的页码,但是由于多次(迭代)调用的关系比yield慢很多。

    yield不同的是yield住后第一次parse函数并没有结束,再次进行的request并不是再次调用第二个函数,而是运行了第一个函数的yield。所以直到第一个parse函数的所有page_list循环完后才会再次调用第二个parse函数。所以limit大于等于3能循环完共42页的数据。

  • 相关阅读:
    C# 1.0 到 C# 3.0 委托创建过程的发展
    C#禁用窗体最大化按钮
    TeamViewer 通过Internet进行远程访问和远程支持
    c# 匿名方法
    BeginInvoke会重新开一个线程
    c# 线程调用带参数的函数
    c# msdn 委托
    判断某张表是否存在在数据库中(access 2003 与sql server 2008)
    新浪微博自动发送微博 功能已实现(net)
    validateRequest="false" 在asp.net 4.0中失效的解决办法
  • 原文地址:https://www.cnblogs.com/khal-Cgg/p/6415560.html
Copyright © 2020-2023  润新知