自动采集文章软件(《神器!五分钟完成大型爬虫项目!》的配置方式)
优采云 发布时间: 2021-12-29 08:20自动采集文章软件(《神器!五分钟完成大型爬虫项目!》的配置方式)
在上一篇《神器!五分钟完成一个大型爬虫项目!》中,我们介绍了一个类似于Scrapy-feapder的开源爬虫框架,重点介绍了该框架的一个应用——AirSpider,它是一个轻量级爬虫。
接下来介绍另一个爬虫应用——Spider,它是一个基于redis的分布式爬虫,适合海量数据采集,支持断点连续爬取、爬虫告警、数据自动存储等功能。
安装
和AirSpider一样,我们也是通过命令行安装的。
由于Spider是分布式爬虫,可能会涉及到多个爬虫,所以最好在一个项目中创建。
创建项目
让我们首先创建项目:
feapder 创建 -p 蜘蛛项目
创建的项目目录如下所示:
项目创建完成后,我们需要在开发时将项目设置为工作间隔,否则编译器在导入非同级目录的文件时会报错。
设置工作间隔方式(以pycharm为例):项目->右键->标记目录为->源根。
创建爬虫
创建爬虫的命令行语句是:
feapder 创建 -s
默认spider_type 值为1。
所以创建Spider的语句是:
feapder 创建 -s spider_test 2
运行该语句后,我们可以看到在spiders目录下生成了spider_test.py文件。
对应的文件内容为:
import feapder
class SpiderTest(feapder.Spider):
# 自定义数据库,若项目中有setting.py文件,此自定义可删除
__custom_setting__ = dict(
REDISDB_IP_PORTS="localhost:6379", REDISDB_USER_PASS="", REDISDB_DB=0
)
def start_requests(self):
yield feapder.Request("https://www.baidu.com")
def parse(self, request, response):
print(response)
if __name__ == "__main__":
SpiderTest(redis_key="xxx:xxx").start()
由于Spider是基于redis分布式的,模板代码默认为redis的配置方式。关于Redis配置信息:
在main函数中,我们可以看到有一个redis_key参数。该参数是redis中存储任务等信息的key前缀。比如redis_key="feapder:spider_test",redis会生成如下:
特征
我们在AirSpider中谈到的方法都是Spider支持的。下面我们来看看Spider和AirSpider的区别。
自动数据存储
写过爬虫的都知道,如果你想把数据持久化到MySQL数据库,字段太多会很烦人。解析后需要手写很多字段,拼凑SQL语句。
Spider帮我们想到了这个问题,我们可以用框架来帮我们自动存入库中。
建一张桌子
第一步,我们需要在数据库中创建一个数据表。这个大家都知道,这里就不多说了。
配置设置
将setting.py中的数据库配置改成自己的配置:
# # MYSQL
MYSQL_IP = ""
MYSQL_PORT =
MYSQL_DB = ""
MYSQL_USER_NAME = ""
MYSQL_USER_PASS = ""
也就是这几个配置。
生成实体类Item
接下来,我们将命令行切换到我们项目的 items 目录并运行命令:
feapder 创建 -i
我这里使用的是数据库中的报表表,所以命令是:
feapder create -i 报告
然后,我们可以在items目录中看到生成的report_item.py实体类。我这里生成的实体类的内容是:
from feapder import Item
class ReportItem(Item):
"""
This class was generated by feapder.
command: feapder create -i report.
"""
__table_name__ = "report"
def __init__(self, *args, **kwargs):
self.count = None
self.emRatingName = None # 评级名称
self.emRatingValue = None # 评级代码
self.encodeUrl = None # 链接
# self.id = None
self.indvInduCode = None # 行业代码
self.indvInduName = None # 行业名称
self.lastEmRatingName = None # 上次评级名称
self.lastEmRatingValue = None # 上次评级代码
self.orgCode = None # 机构代码
self.orgName = None # 机构名称
self.orgSName = None # 机构简称
self.predictNextTwoYearEps = None
self.predictNextTwoYearPe = None
self.predictNextYearEps = None
self.predictNextYearPe = None
self.predictThisYearEps = None
self.predictThisYearPe = None
self.publishDate = None # 发表时间
self.ratingChange = None # 评级变动
self.researcher = None # 研究员
self.stockCode = None # 股票代码
self.stockName = None # 股票简称
self.title = None # 报告名称
如果字段有默认值或自增,默认会被注释掉,可以按需打开。可以看到我的表的id字段是在这里注释的。
如果item字段太多,又不想一一赋值,可以如下创建:
feapder create -i 报告 1
此时生成的实体类是这样的:
class ReportItem(Item):
"""
This class was generated by feapder.
command: feapder create -i report 1.
"""
__table_name__ = "report 1"
def __init__(self, *args, **kwargs):
self.count = kwargs.get('count')
self.emRatingName = kwargs.get('emRatingName') # 评级名称
self.emRatingValue = kwargs.get('emRatingValue') # 评级代码
self.encodeUrl = kwargs.get('encodeUrl') # 链接
# self.id = kwargs.get('id')
self.indvInduCode = kwargs.get('indvInduCode') # 行业代码
self.indvInduName = kwargs.get('indvInduName') # 行业名称
self.lastEmRatingName = kwargs.get('lastEmRatingName') # 上次评级名称
self.lastEmRatingValue = kwargs.get('lastEmRatingValue') # 上次评级代码
self.orgCode = kwargs.get('orgCode') # 机构代码
self.orgName = kwargs.get('orgName') # 机构名称
self.orgSName = kwargs.get('orgSName') # 机构简称
self.predictNextTwoYearEps = kwargs.get('predictNextTwoYearEps')
self.predictNextTwoYearPe = kwargs.get('predictNextTwoYearPe')
self.predictNextYearEps = kwargs.get('predictNextYearEps')
self.predictNextYearPe = kwargs.get('predictNextYearPe')
self.predictThisYearEps = kwargs.get('predictThisYearEps')
self.predictThisYearPe = kwargs.get('predictThisYearPe')
self.publishDate = kwargs.get('publishDate') # 发表时间
self.ratingChange = kwargs.get('ratingChange') # 评级变动
self.researcher = kwargs.get('researcher') # 研究员
self.stockCode = kwargs.get('stockCode') # 股票代码
self.stockName = kwargs.get('stockName') # 股票简称
self.title = kwargs.get('title') # 报告名称
这样,当我们请求返回的json数据时,就可以直接赋值了,比如:
response_data = {"title":" 测试"} # 模拟请求回来的数据
item = SpiderDataItem(**response_data)
希望数据自动存储在数据库中也相对简单。解析完数据后,将数据赋值给Item,然后yield:
def parse(self, request, response):
html = response.content.decode("utf-8")
if len(html):
content = html.replace('datatable1351846(', '')[:-1]
content_json = json.loads(content)
print(content_json)
for obj in content_json['data']:
result = ReportItem()
result['orgName'] = obj['orgName'] #机构名称
result['orgSName'] = obj['orgSName'] #机构简称
result['publishDate'] = obj['publishDate'] #发布日期
result['predictNextTwoYearEps'] = obj['predictNextTwoYearEps'] #后年每股盈利
result['title'] = obj['title'] #报告名称
result['stockName'] = obj['stockName'] #股票名称
result['stockCode'] = obj['stockCode'] #股票code
result['orgCode'] = obj['stockCode'] #机构code
result['predictNextTwoYearPe'] = obj['predictNextTwoYearPe'] #后年市盈率
result['predictNextYearEps'] = obj['predictNextYearEps'] # 明年每股盈利
result['predictNextYearPe'] = obj['predictNextYearPe'] # 明年市盈率
result['predictThisYearEps'] = obj['predictThisYearEps'] #今年每股盈利
result['predictThisYearPe'] = obj['predictThisYearPe'] #今年市盈率
result['indvInduCode'] = obj['indvInduCode'] # 行业代码
result['indvInduName'] = obj['indvInduName'] # 行业名称
result['lastEmRatingName'] = obj['lastEmRatingName'] # 上次评级名称
result['lastEmRatingValue'] = obj['lastEmRatingValue'] # 上次评级代码
result['emRatingValue'] = obj['emRatingValue'] # 评级代码
result['emRatingName'] = obj['emRatingName'] # 评级名称
result['ratingChange'] = obj['ratingChange'] # 评级变动
result['researcher'] = obj['researcher'] # 研究员
result['encodeUrl'] = obj['encodeUrl'] # 链接
result['count'] = int(obj['count']) # 近一月个股研报数
yield result
返回item后,item会流入框架的ItemBuffer。每 0.05 秒或当项目数累积到 5000 时,这些项目将分批进入数据库。对于表名,从类名中删除 Item 的小写。例如,ReportItem 数据将落入报告表中。
调试
在开发过程中,我们可能需要调试某个请求。通常的做法是修改下发任务的代码。但这并不好。一遍遍的改可能把之前写的逻辑搞乱,或者忘记改回去直接发布,或者调试数据存放在数据库中,污染了库中已有的数据,造成了很多不该出现的问题t 已经发生。
该框架支持Debug爬虫,可以调试某个任务。写法如下:
if __name__ == "__main__":
spider = SpiderTest.to_DebugSpider(
redis_key="feapder:spider_test", request=feapder.Request("http://www.baidu.com")
)
spider.start()
对比之前的启动方式:
spider = SpiderTest(redis_key="feapder:spider_test")
spider.start()
可以看到,代码中的to_DebugSpider方法可以直接将原来的爬虫转化为调试爬虫,然后通过传递request参数来抓取指定的任务。
调试通常与断点结合使用。在调试模式下,运行产生的数据默认不存储在库中。
除了指定 request 参数,还可以指定 request_dict 参数。request_dict接收字典类型,如request_dict={"url":""},与请求的传输一致。request 或 request_dict 都可以传递。
运行多个爬虫
通常,一个项目下可能有多个爬虫。为规范起见,建议将启动项放在项目下的main.py中,然后通过命令行运行指定文件。
例如,以下项目:
项目收录
两个spider,main.py写成如下:
from spiders import *
from feapder import Request
from feapder import ArgumentParser
def test_spider():
spider = test_spider.TestSpider(redis_key="spider:report")
spider.start()
def test_spider2():
spider = test_spider.TestSpider2(redis_key="spider:report")
spider.start()
if __name__ == "__main__":
parser = ArgumentParser(description="Spider测试")
parser.add_argument(
"--test_spider", action="store_true", help="测试Spider", function=test_spider
)
parser.add_argument(
"--test_spider2", action="store_true", help="测试Spider2", function=test_spider2
)
parser.start()
这里使用了 ArgumentParser 模块来支持命令行参数,比如运行 test_spider:
python3 main.py --test_spider
分散式
说白了,分布式就是启动多个进程来处理同一批任务。Spider支持多次启动,不重复任务。我们可以在多台服务器上部署和启动它,或者在同一台机器上多次启动它。
总结
说到这里,我们就说完Spider分布式爬虫。还有一些细节,使用的时候需要考虑一下。总的来说,这个框架比较好用。它易于使用,足以处理一些不是很复杂的场景。你可以尝试重构你的爬虫,试试这个框架。