您的位置:首页 > 数据库 > MySQL

Scrapy爬数据并存储到mysql中

2017-08-14 14:11 393 查看

前言

看这篇文嘉,假设你已经:

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项目:



  我们主要用的就是这四个文件: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

4000
#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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  python 数据 mysql