Scrapy爬数据并存储到mysql中

前言

看这篇文嘉,假设你已经:
1. 安装了Srcapy框架
2. 安装了mysql
3. import了pymysql
后面两个可以参考我的前一篇博客,请移步:Python3.x连接Pymysql

首先来说说我踩的坑

坑1

  我定义好items后,下一步是要将items这个容器引入到我的spider里面。所以要在spider文件中写上这么一句话:

from scrapytest.CourseItems import CourseItem

  scrapytest是我的工程名,CourseItem是我的item的类名。但是这句话在我的notebook里面死活给我报错:

No Module named "scrapytest"

  What?我的scrapytest好好的在那啊。然后我建立一个同层的py文件,就是好的,所以我推测可能在notebook上面没有这种导入吧。

坑2

  我最后爬的数据,根本就存储不到数据库中,所以我检查了几个步骤来排查原因:

1.到底有没有爬到数据?

  我写了一个生成json文件的类,然后去看了我保存下来的json,里面是有数据的,所以是数据库插入的问题。

2.在连接“数据池“的时候出现了问题?

  我在连接函数的内部打了log,结果可以打印log,所以连接数据库没问题。

3.数据库启动了没?

  我重新写了一个简单的插入,用的是这里同样的代码,不过是把相对的数据改为绝对的数据。结果是可以存储的。所以得出结论,数据库人家好好的。

4.数据的问题?

  由上一步的一些结论,我发现我的相对数据不行,但是改成绝对数据的话,就可以存储。所以我的数据格式有问题。我打印了我的数据,发现都是list,而且price还是list里面套着list。那么就知道是price的格式不对,导致我老是报错:(此处感谢帮我调了一天bug的orangesdk[可调戏])


这里写图片描述

  说我没有送进去数据,它必须要一个数据才行。所以我追根溯源,去看price爬取的时候我写的xpath:

item['price'] = response.xpath('//div[@class="item-mod"]/div[@class="favor-pos"]/p[@class="price"]/span/text()').extract()

  我想着数组里面套数组,肯定就是数据爬多了,所以得再精确地定位一下这个price。把前面的//div[@class=”item-mod”]删除:

item['price'] = response.xpath('//p[@class="price"]/span/text()').extract()

  price格式好了之后,就可以插入了。

我们开始建立Srcapy项目

  安装Scrapy这里就不再赘述了,然后就是执行以下命令新建一个Scrapy项目:

               scrapy startproject scrapytest

【scrapy是我项目的名字,可以任意更改。】
  然后就可以看到我们生成好的一个scrapy项目:


Scrapy爬数据并存储到mysql中_第1张图片

  我们主要用的就是这四个文件:items、pipelines.py、settings.py、spiders下面的spider.py。先分别说说它们怎么用:

1.items.py:

  定义你想要爬的东西的名字:

import scrapy

class HouseItem(scrapy.Item):
    #名称
    name = scrapy.Field()
    #区域
    region = scrapy.Field()
    #价格
    price= scrapy.Field()

  等号后面的scrapy.Field()是固定搭配,HouseItem是我的item的类名,其实我这个文件叫做HouseItem.py,我在item.py的同一层新建的,跟直接在item.py里面写的效果是一样的。

2.pipelines.py

  pipelines里面是写其他操作类的。比如写一个类专门向数据库中插值,一个类专门生成json。向数据库中插值代码如下:

from scrapy import log
import pymysql
import pymysql.cursors
import codecs
from twisted.enterprise import adbapi

class WebcrawlerScrapyPipeline(object):

    @classmethod
    def from_settings(cls, settings):
        dbargs = dict(
            host=settings['MYSQL_HOST'],
            db=settings['MYSQL_DBNAME'],
            user=settings['MYSQL_USER'],
            passwd=settings['MYSQL_PASSWD'],
            port=settings['MYSQL_PORT'],
            charset='utf8',
            cursorclass=pymysql.cursors.DictCursor,
            use_unicode=True,
        )
        dbpool = adbapi.ConnectionPool('pymysql', **dbargs)
        return cls(dbpool)


    def __init__(self,dbpool):
        self.dbpool=dbpool

    #pipeline默认调用
    def process_item(self, item, spider):
        d=self.dbpool.runInteraction(self._conditional_insert, item, spider)#调用插入的方法
        log.msg("-------------------连接好了-------------------")
        d.addErrback(self._handle_error,item,spider)#调用异常处理方法
        d.addBoth(lambda _: item)
        return d

    def _conditional_insert(self, conn, item, spider):
        log.msg("-------------------打印-------------------")

        conn.execute("insert into test (name, region, price) values(%s, %s, %s)",
                     (item['name'], item['region'], item['price']))
        log.msg("-------------------一轮循环完毕-------------------")
    def _handle_error(self, failue, item, spider):
        print(failue)

  我们可以改的东西:

  • 类名可以改
  • cursorclass=pymysql.cursors.DictCursor里面的pymysql,是因为我用的是pymysql,如果你是python3.4以前的版本的话,你可以使用MySQLDB。
  • dbpool = adbapi.ConnectionPool('pymysql', **dbargs)这个里面的pymysql同上.
  • “_conditional_insert” 这个方法的方法名可以改,如果更改的话,上面的d=self.dbpool.runInteraction(self._conditional_insert, item, spider)调用的名字也要改.
  • “_conditional_insert” 插入的sql语句:
conn.execute("insert into test (name, region, price) values(%s, %s, %s)",(item['name'], item['region'], item['price']))

  这句话一定要写对。test是表名,在执行这个操作之前,你要确保你的数据库里面有这个表。表里面的字段有你item里面的那几个字段,这些都是一一对应的。

3.settings.py

  settings最重要的就是要加上两个设置:

  • 引入前面你写的类,并附上优先级(后面的数字就是优先级,数字越小越优先执行)。
ITEM_PIPELINES = {
    'scrapytest.MyPipelines.WebcrawlerScrapyPipeline': 300,
    'scrapytest.MyPipelines.MyPipeline': 1,
}

  • 添加数据库信息。如果是本地的,就写上localhost,如果连接的是远程的mysql数据库,则直接写远程的ip。
MYSQL_HOST = 'localhost'
MYSQL_DBNAME = 'test'         #数据库名字,请修改
MYSQL_USER = 'root'             #数据库账号,请修改
MYSQL_PASSWD = 'root'         #数据库密码,请修改
MYSQL_PORT = 3306               #数据库端口

4.spiders下面的spider.py

import scrapy
from scrapytest.HouseItems import HouseItem

class MySpider(scrapy.Spider):
    name = "MySpider"
    # 设定域名
    allowed_domains = ["anjuke.com"]
    # 填写爬取地址
    start_urls = [
        'https://xa.fang.anjuke.com/loupan/all/p%s/' % p for p in range(1, 15)
    ]

    # 编写爬取方法
    def parse(self, response):
        # 实例一个容器保存爬取的信息
        item = CourseItem()
        # 这部分是爬取部分,使用xpath的方式选择信息,具体方法根据网页结构而定
        item['name'] = response.xpath('//div[@class="infos"]/div[@class="lp-name"]/h3/a[@class="items-name"]/text()').extract()

        item['region'] = response.xpath('//div[@class="infos"]/p[@class="address"]/a[@class="list-map"]/text()').extract()

        item['price'] = response.xpath('//p[@class="price"]/span/text()').extract()
        yield item

需要注意:

  • 一定要引入你的Items文件,如果不引入会不知道你要爬哪个,会说你下面的item都没有定义。
  • 一定要写上name。这里我的name是叫“MySpider”。因为有时候可能会并行几个爬虫,名字可以用来区分。而且当你运行整个项目的时候,也是直接运行爬虫。执行的命令行需要输入名字。
  • item一定要先初始化。item = HouseItem()。给它初始化一下,才能知道后面我们定义的都是什么。
  • Xpath一定要写对。Xpath的语法网上有很多,这里不再详述,可以自行百度。(我往往出错都是Xpath没写对= =)
  • Xpath后面不要忘记写.extract()。
  • 最后来一个yield item一定不能忘记。yield的意思是把你获取的抛出后,继续执行这个函数。和return的区别就是会继续执行,而return不会继续。
  • ## 运行我们的Srcapy项目
      进入项目目录之后,执行:
                       Scrapy crawl MySpider

  就可以在数据库中看到结果了。看不到结果的,请参照我前面写的坑2的调试方法。

                                memoryjdch编辑于2017.8.18

你可能感兴趣的:(实战项目)