声明:仅供技术交流,请勿用于非法用途,如有其它非法用途造成损失,和本文无关
前言
废话不多说,直接开始吧~本次爬取的网站是:有缘网
一、基本配置
一台master机,一台slave机,本文使用的master是云服务器(Ubuntu),而slave是我的笔记本电脑(Windows)
两台机都需要安装Python3.7、Scrapy框架、Scrapy-Redis框架
在master机上安装Redis数据库,并配置好能够远程连接等等
二、分析页面
我们随便筛选几个条件搜索一下,发现了这个列表页的url是存在一定规律的:例如:筛选的条件为广东的18岁以上的mm:
http://www.youyuan.com/find/guangdong/mm18-0/advance-0-0-0-0-0-0-0/p1/
例如:筛选的条件为广东的18岁至25岁的mm:
http://www.youyuan.com/find/guangdong/mm18-25/advance-0-0-0-0-0-0-0/p1/
其次,按F12打开开发者工具,然后可以看到这一列表页上的所有信息都在四、开始敲代码
首先创建Scrapy爬虫项目:scrapy startproject youyuan
然后再创建爬虫文件:cd youyuan && scrapy genspider yy youyuan.com
最后开始敲代码
(一)原始Scrapy框架下的爬虫代码(非分布式)
items.py 代码如下:
from scrapy import Item, Field
class YouyuanItem(Item):
name=Field() #姓名
address = Field() #地址
age = Field() #年龄
height = Field() #身高
salary = Field() #收入
house = Field() #房子
hobby = Field() #爱好
image = Field() #照骗
motto = Field() #内心独白
detail = Field() #详细资料
boy_condition = Field() #男友标准
url=Field() #个人主页
pipelines.py 代码如下:(开发阶段只先输出一下,没有特别的地方)
# -*- coding: utf-8 -*-
# Define your item pipelines here
class YouyuanPipeline(object):
def process_item(self, item, spider):
if spider.name == 'yy':
print(item)
return item
yy.py 代码如下:
# -*- coding: utf-8 -*-
import scrapy
from youyuan.items import YouyuanItem
import re
# 这个函数是用于处理详细资料和征友条件,返回一个字典
def process_data(data):
key = data[::2]
value = [re.sub(' ','',i) for i in data[1::2]]
return dict(zip(key, value))
class YySpider(scrapy.Spider):
name = 'yy'
allowed_domains = ['youyuan.com']
start_urls = ['http://www.youyuan.com/city/']
def parse(self, response):
# 构造每个城市中的18岁以上的mm请求url
base_url = 'http://www.youyuan.com/find{0}mm18-0/advance-0-0-0-0-0-0-0/p1/'
a_list=response.xpath('//div[@class="yy_city_info"]/ul/li/font/a')
for a in a_list:
yield scrapy.Request(
base_url.format(a.xpath('./@href').get()),
callback=self.parse_all,
priority=3
)
def parse_all(self, response):
# 拿到列表页中所有mm的详情页url
li_list = response.xpath('//ul[@class="mian search_list"]/li')
for li in li_list:
detail_url = response.urljoin(li.xpath('./dl/dt/a/@href').get())
yield scrapy.Request(
detail_url,
callback=self.parse_item,
priority=1
)
# 若有下一页,则继续发起请求
temp = response.xpath('//a[@class="pe_right"]/@href').get()
if temp != '###':
next_page = response.urljoin(temp)
yield scrapy.Request(
next_page,
callback=self.parse_all,
priority=2
)
def parse_item(self, response):
# 详情页里爬取mm的所有想要的信息
# 判断是否已经进入了详情页,有一些页面会重定向到首页,从而会报错的
flag=response.xpath('//p[@class="top_tit"]')
if flag:
item=YouyuanItem()
item['name'] = response.xpath('//div[@class="main"]/strong/text()').get() # 姓名
temp=response.xpath('//p[@class="local"]/text()').get().split()
item['address'] = temp[0] # 地址
item['age'] = temp[1] # 年龄
item['height'] = temp[2] # 年龄
item['salary'] = temp[3] # 收入
item['house'] = temp[4] # 房子
item['hobby'] = [i.strip() for i in response.xpath('//ol[@class="hoby"]/li//text()').getall()] # 爱好
item['image'] = response.xpath('//li[@class="smallPhoto"]/@data_url_full').getall() # 照骗
item['motto'] = response.xpath('//ul[@class="requre"]/li[1]/p/text()').get().strip() # 内心独白
item['detail'] = process_data(response.xpath('//div[@class="message"]')[0].xpath('./ol/li//text()').getall()) # 详细资料
item['boy_condition'] = process_data(response.xpath('//div[@class="message"]')[1].xpath('./ol/li//text()').getall()) # 男友标准
item['url'] = response.url # 个人主页
return item
settings.py 代码如下:
# -*- coding: utf-8 -*-
# Scrapy settings for youyuan project
BOT_NAME = 'youyuan'
SPIDER_MODULES = ['youyuan.spiders']
NEWSPIDER_MODULE = 'youyuan.spiders'
# 日志级别
LOG_LEVEL='DEBUG'
# 用户代理
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36'
# Obey robots.txt rules
ROBOTSTXT_OBEY = False
# 下载延迟
DOWNLOAD_DELAY = 0.5
# pipeline管道
ITEM_PIPELINES = {
'youyuan.pipelines.YouyuanPipeline': 300,
}
(二)Scrapy-Redis(分布式爬虫)
其实应用Scrapy-Redis框架来写分布式爬虫,只需要在原始Scrapy框架下的爬虫代码里修改一部分代码即可。
from scrapy_redis.spiders import RedisSpider
# class YySpider(scrapy.Spider):
class YySpider(RedisSpider):
name = 'yy'
allowed_domains = ['youyuan.com']
# start_urls = ['http://www.youyuan.com/city/']
redis_key = 'yy:start_urls'
# 去重
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
# 调度器
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# 优先级队列(以下3个随便选一个)
# SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderPriorityQueue"
SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderQueue"
#SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderStack"
# 允许暂停
SCHEDULER_PERSIST = True
# Redis连接
REDIS_HOST = 'master机的ip地址'
REDIS_PORT = 6379
REDIS_PARAMS = {'password':'redis设置的密码'} # 没有配置密码的话可以去掉
# 并发请求数量(默认是16,若不更改小一点的话,会出现有一台机一直处于等待状态)
CONCURRENT_REQUESTS = 2
# pipeline管道(开启redis的管道)
ITEM_PIPELINES = {
'youyuan.pipelines.YouyuanPipeline': 300,
'scrapy_redis.pipelines.RedisPipeline': 400,
}
五、运行分布式爬虫项目
首先,在master机上开启redis服务端,即在控制台运行命令:redis-server奇迹服务端修改教程,或者以指定配置文件的方式来启动:redis-server /etc/redis/redis.conf
然后,在master机上的另一个终端上进入redis客户端,即在控制台运行命令:redis-cli,之后如果你的redis设置了密码,那么再输入:auth 你的密码,然后才能继续操作redis数据库
再者,分别运行两台机的爬虫项目(不分先后,随意即可),运行的命令是:scrapy crawl yy(在有scrapy.cfg文件的目录下进入cmd命令),此时两台机都是处于监听的状态中
最后,在master机的redis客户端中,输入:lpush yy:start_urls ,之后,会看到两台机都开始跑起来了!ohhhhhhhhhhh~
六、将数据转存至MySQL
即使爬取到的数据会自动存到Redis数据库中(开启Redis的pipeline之后),可是我的云服务器内存还是比较小,不想占用太多的内存,所以,可以将Redis数据库中的数据转存到本地MySQL数据库中。
Python中操作Redis数据库的包是:redis,可以直接用pip来安装;而操作MySQL数据库的包是:pymysql,同样可以用pip来安装。
将数据转存到MySQL的代码如下:
import json
import redis
import pymysql
import time
# 连接redis数据库
rediscli = redis.StrictRedis(host='', port = 6379, db = 0, password='')
# 连接mysql数据库
mysqlcli = pymysql.connect(host='',user='',password='',database='',charset='utf8')
while True:
if rediscli.exists("yy:items") == 0:
sj = time.perf_counter() # 计时
# 超过1分钟,退出循环
if sj >= 60:
mysqlcli.close()
break
else:
# 将Redis数据库中的数据pop出来
source, data = rediscli.blpop(["yy:items"])
item = json.loads(data)
cursor = mysqlcli.cursor()
sql = '''insert into youyuan(name,address,age,height,salary,house,hobby,image,motto,
detail,boy_condition,url) values (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)'''
cursor.execute(sql,[item['name'],item['address'],item['age'],item['height'],item['salary'],item['house'],
json.dumps(item['hobby'],ensure_ascii=False),
json.dumps(item['image'],ensure_ascii=False),
item['motto'],json.dumps(item['detail'],ensure_ascii=False),
json.dumps(item['boy_condition'],ensure_ascii=False),item['url']])
mysqlcli.commit()
cursor.close()
七、一些tips、一些坑
在本机上先敲好代码,然后打包项目,直接上传至云服务器即可。比如打包时的命令:tar -cvf youyuan.tar youyuan(在youyuan项目的最上层文件目录下进入cmd命令,再运行即可);然后在云服务器上解包的命令:tar -xvf youyuan.tar即可;其中,将打包后项目上传到云服务器上可以在xshell里面操作。
Redis数据库的安装以及配置,可以看这里的教程点击跳转,还有,可能在redis运行的日志文件中会发现有个警告:WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.这时奇迹服务端修改教程,只需按照它的提示在这个/etc/sysctl.conf配置文件中加入vm.overcommit_memory = 1,并且在控制台中运行命令:sysctl vm.overcommit_memory=1即可。
这个有缘网站应该是有限制可查看数量的,因为一开始我只是想爬取全国18岁~25岁的mm的,但是跑完之后才1000左右条数据,而且我发现直接跳到几百页后,会有重复的数据出现,所以说那些页数都是假的?!所以我才一怒之下将25岁的上限给去掉了(捂脸)。不过也有可能是我没有注册,可能登陆之后会看到更多的信息??这个后面再说吧哈哈。
参考链接
写在最后
其实一开始我是用RedisCrawlSpider来爬的,可是呢,如果只是一台机运行的话,master和salve都可以跑,没问题的,但是,一旦两台机一起跑,就会有其中一台机跑不了,报错的信息是builtins.ValueError:Method '_callback' not found,不是callback找不到就是其他的函数,总之总有一台机跑不了(百度了很久,也没有解决。),本来我都想放弃了,然后突然有个想法就是不用RedisCrawlSpider来爬,换成RedisSpider,之后竟然奇迹般地成功了!!哈哈哈哈~
这个是补全之前的Scrapy框架的学习心得,其实在那个时候就知道分布式爬虫的一些原理了,只是没有去实践。所以,现在有时间了,就补上了之前学习Scrapy框架未完成的事。还是会学到很多东西的哈哈~
本文首发于我的CSDN博客。
ps:【Python王者之后】公众号中回复:20200626即可获取本文项目的源代码
版权声明
本文仅代表作者观点。
本文系作者授权发表,未经许可,不得转载。
发表评论