前言:本案例将分为三篇。
第一篇,使用scrapy框架来实现爬取中国红娘相亲网站。
第二篇,使用scrapy-redis 简单的方式爬取中国红娘相亲网站。(使用redis存储数据,请求具有持续性,但不具备分布式)
第三篇,使用scrapy-redis 分布式的方法爬取中国红娘相亲网站,并使用mysql将数据持久化。
废话不多说,从settings.py文件配置来说,没有什么变化。主要改写spider 即可。
【1】导入RedisCrawlSpider类,不使用CrawlSpider
from scrapy_redis.spiders import RedisCrawlSpider
【2】修改继承的类为 RedisCrawlSpider
class HongniangspiderSpider(RedisCrawlSpider):
【3】修改爬虫爬取的域(allowed_domains)和开始的url(start_urls)
# allowed_domains = ['hongniang.com']
# start_urls = ['http://www.hongniang.com/index/search?sort=0&wh=0&sex=2&starage=0&province=0&city=0&marriage=0&edu=0&income=0&height=0&pro=0&house=0&child=0&xz=0&sx=0&mz=0&hometownprovince=0']
# 增加redis_keys
redis_key = 'hongniang:start_urls'
我们可以直接将 爬取的域和 开始的url 注释掉,添加上一个 redis_key ,通常这个reidskey 的写法就是: 爬虫名:start_urls
在reids 中,“:” 冒号将被生成像文件夹一样。
这个redis_key 设置的就是我们将来开始爬取的网址,即跟原来的 start_urls 一样。
其中allowed_domains 允许的域,如果不写方法设置,那么默认自动获取网址中的域。
修改基本就是这么多
整体代码:
# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from hongniang.items import HongniangItem
# 1. 导入RedisCrawlSpider类,不使用CrawlSpider
from scrapy_redis.spiders import RedisCrawlSpider
class HongniangspiderSpider(RedisCrawlSpider):
name = 'hongniangSpider'
# allowed_domains = ['hongniang.com']
# start_urls = ['http://www.hongniang.com/index/search?sort=0&wh=0&sex=2&starage=0&province=0&city=0&marriage=0&edu=0&income=0&height=0&pro=0&house=0&child=0&xz=0&sx=0&mz=0&hometownprovince=0']
# 增加redis_keys
redis_key = 'hongniang:start_urls'
#中国红娘index页面的分页
page_lx = LinkExtractor(allow=('index/search\?.*&page=\d+'))
#个人详细的信息
self_lx = LinkExtractor(allow=('user/member/id/\d+'))
#规则
rules = (
Rule(page_lx,follow=True),
Rule(self_lx,callback='parse_item',follow=False)
)
def parse_item(self, response):
item = HongniangItem()
# 用户名称
item['nickname'] = self.get_nickname(response)
# 用户id
item['loveid'] = self.get_loveid(response)
# 用户的照片
item['photos'] = self.get_photos(response)
# 用户年龄
item['age'] = self.get_age(response)
# 用户的身高
item['height'] = self.get_height(response)
# 用户是否已婚
item['ismarried'] = self.get_ismarried(response)
# # 用户年收入
item['yearincome'] = self.get_yearincome(response)
# # 用户的学历
item['education'] = self.get_education(response)
# # 用户的地址
item['workaddress'] = self.get_workaddress(response)
# 用户的内心独白
item['soliloquy'] = self.get_soliloquy(response)
# 用户的性别
item['gender'] = self.get_gender(response)
print item
yield item
def get_nickname(self,response):
nickname = response.xpath('//div[@class="info1"]/div[@class="name nickname"]/text()').extract()[0]
if len(nickname)>0:
nickname = nickname.strip()
else:
nickname = "NULL"
return nickname
def get_loveid(self, response):
loveid = response.xpath('//div[@class="info1"]/div[@class="loveid"]/text()').extract()[0]
if len(loveid) > 0:
loveid = loveid.strip()
else:
loveid = "NULL"
return loveid
def get_photos(self, response):
photos = response.xpath('//div[@id="tFocus-btn"]/ul/li/img/@src').extract()
if len(photos) > 0:
pass
else:
photos = "NULL"
return photos
def get_age(self, response):
age = response.xpath('//div[@class="info2"]/div/ul/li/text()').extract()[0]
if len(age) > 0:
age = age.strip()
else:
age = "NULL"
return age
def get_height(self, response):
height = response.xpath('//div[@class="info2"]/div/ul/li/text()').extract()[2]
if len(height) > 0:
height = height.strip()
else:
height = "NULL"
return height
def get_ismarried(self, response):
ismarried = response.xpath('//div[@class="info2"]/div/ul/li/text()').extract()[1]
if len(ismarried) > 0:
ismarried = ismarried.strip()
else:
ismarried = "NULL"
return ismarried
def get_yearincome(self, response):
yearincome = response.xpath('//div[@class="info2"]/div/ul/li/text()').extract()[4]
if len(yearincome) > 0:
yearincome = yearincome.strip()
else:
yearincome = "NULL"
return yearincome
def get_education(self, response):
education = response.xpath('//div[@class="info2"]/div/ul/li/text()').extract()[3]
if len(education) > 0:
education = education.strip()
else:
education = "NULL"
return education
def get_workaddress(self, response):
workaddress = response.xpath('//div[@class="info2"]/div/ul/li/text()').extract()[5]
if len(workaddress) > 0:
workaddress = workaddress.strip()
else:
workaddress = "NULL"
return workaddress
def get_soliloquy(self, response):
soliloquy = response.xpath('//div[@class="info5"]/div[@class="text"]/text()').extract()[0]
if len(soliloquy) > 0:
soliloquy = soliloquy.strip()
else:
soliloquy = "NULL"
return soliloquy
def get_gender(self, response):
return "女"
在启动之前, 由于上一篇我们曾使用基础的redis 爬虫爬取过一次,我们先删除之前爬取的数据,在redis-manager 中删除即可。如果没有就没事。
启动该工程需要两步:
第一步,先启动我们的刚才写好的爬虫程序,跟原来的启动方式不一样。
scrapy runspider 爬虫文件名.py
在我们这里就是 scrapy runspider hongniangSpider_redis2.py ,注意不是爬虫的类名,而是文件名
当我们的爬虫启动好之后,会出现等待。在等待开始的url 到来。
第二步,在redis 中设置开始的url
先进入redis 数据库
redis-cli -h 127.0.0.1 -p 6379
lpush 一下开始的url
lpush hongniang:start_urls http://xxx
当我们设置成功以后,爬虫程序就开始跑起来了。
我们也来看下,redis 数据库中的变化。
【1】先新建mysql数据库表,将我们item 中的字段设置到表中:
如果懒得创建,可以直接下载:https://github.com/gengzi/hongniang/blob/master/hongniang.sql
【2】编写代码,这一部分跟爬虫没有关系,可以直接写在爬虫外面运行即可。看代码吧。
mysqlHelper.py 主要是一个工具,方便对mysql 操作。
# -*- coding:utf-8 -*-
# Hello world - 西蒙.科泽斯 这是“向编程之神所称颂的传统咒语,愿他帮助并保佑你更好的学习这门语言
import MySQLdb
class MysqlHelper():
"""
mysql 工具类
2018年2月26日14:32:34
"""
def __init__(self,host,port,db,user,passwd,charset='utf8'):
self.host=host
self.port=port
self.db=db
self.user=user
self.passwd=passwd
self.charset=charset
def connect(self):
self.conn=MySQLdb.connect(host=self.host,port=self.port,db=self.db,user=self.user,passwd=self.passwd,charset=self.charset)
self.cursor=self.conn.cursor()
def close(self):
self.cursor.close()
self.conn.close()
def get_one(self,sql,params=()):
result=None
try:
self.connect()
self.cursor.execute(sql, params)
result = self.cursor.fetchone()
self.close()
except Exception, e:
print e.message
return result
def get_all(self,sql,params=()):
list=()
try:
self.connect()
self.cursor.execute(sql,params)
list=self.cursor.fetchall()
self.close()
except Exception,e:
print e.message
return list
def insert(self,sql,params=()):
return self.__edit(sql,params)
def update(self, sql, params=()):
return self.__edit(sql, params)
def delete(self, sql, params=()):
return self.__edit(sql, params)
def __edit(self,sql,params):
count=0
try:
self.connect()
count=self.cursor.execute(sql,params)
self.conn.commit()
self.close()
except Exception,e:
print e.message
return count
ReidsToMysql.py 就是将redis 数据 转存到 mysql 中
#!/usr/bin/env python2
# -*- coding:utf-8 -*-
import redis
import json
from MysqlHelper import MysqlHelper
import sys
reload(sys)
sys.setdefaultencoding('utf8')
#获取mysql 的连接
mysql = MysqlHelper(host='123.206.30.117',port=3306,db='pythonspider',user='root',passwd='111')
#获取redis 的连接
rediscli = redis.StrictRedis(host='127.0.0.1', port = 6379, db = 0)
def process_redisdata():
"""
将redis 的数据,存放到mysql中
:return:
"""
while True:
# FIFO模式为 blpop,LIFO模式为 brpop,获取键值
source, data = rediscli.blpop(["hongniangSpider:items"])
#data = unicode(str(data).encode('utf-8'), "utf-8")
item = json.loads(data)
photos = ','.join(item['photos'])
sql = 'insert into hongniang(nickname,loveid,photos,age,height,ismarried,yearincome,education,workaddress,soliloquy,gender) ' \
'values(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)';
params = [str(item['nickname']),str(item['loveid']),str(photos),str(item['age'])
,str(item['height']),str(item['ismarried']),str(item['yearincome']),str(item['education'])
, str(item['workaddress']),str(item['soliloquy']),str(item['gender'])]
insertnum = mysql.insert(sql=sql,params=params)
if insertnum > 0:
print "成功"
if __name__ == '__main__':
process_redisdata()
直接运行 redistomap 这个py 文件即可。
查看我们保存的数据:
到这里,这个爬取中国红娘相亲网站基本就结束了,写的可能不是很清楚,可以参考源码再看文章。感觉不错就点个赞吧。
有什么问题可以提出,不胜感激。
源码:https://github.com/gengzi/hongniang