首页 > 代码库 > 爬虫--scrapy

爬虫--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页的数据。

 

爬虫--scrapy