自动采集文章软件(《神器!五分钟完成大型爬虫项目!》的配置方式)

优采云 发布时间: 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分布式爬虫。还有一些细节,使用的时候需要考虑一下。总的来说,这个框架比较好用。它易于使用,足以处理一些不是很复杂的场景。你可以尝试重构你的爬虫,试试这个框架。

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线