前言
看这篇文嘉,假设你已经:
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
#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