Pyqt5和Scrapy开发可视化爬虫

做了一个电商的爬虫,仅做学习使用

Pyqt5和Scrapy开发可视化爬虫_第1张图片

记录一下遇到的坑:
整个开发步骤为,利用scrapy爬几大电商网站的商品图片,然后开发UI,根据输入的信息,执行不同的爬虫。坑就主要在这个地方

1. 如何向scrapy爬虫传递配置信息,包括要爬取的电商网站(即要启动的spider),商品名称,数据库信息,图片保存路径?(不同的爬虫使用不同的配置)

scrapy框架,在settings.py中设置整个项目的配置信息,项目里面所有的spider公用一个配置,不同的爬虫,需要使用不同的配置,即使用不同的settings启动爬虫:

2019年4月23日更新

scrapy框架中,settings有其优先级,官方文档(scrapy-settings)中有如下说明:

设定可以通过多种方式设置,每个方式具有不同的优先级。 下面以优先级降序的方式给出方式列表:
	1. 命令行选项(Command line Options)(最高优先级)
	2. 每个spider的设定(customer_settings)
	3. 项目设定模块(Project settings module)
	4. 命令默认设定模块(Default settings per-command)
	5. 全局默认设定(Default global settings) (最低优先级)
1. 命令行指定设置(文档)

例:scrapy crawl myspider -s LOG_FILE=log.log -s IMAGES_SCORE=D:/images

2. 使用customer_settings

与命令行传参类似,通过custom_settings可以在spider中传入自定义的参数来修改settings

class JdSpider(scrapy.Spider):
    name = 'jd'
    allowed_domains = ['jd.com', 'p.3.cn', 'jd.hk']
    def __init__(self, name, images_store, host, port, username, password, db_name, ):
        self.keyword = name
         # 动态配置settings
        self.custom_settings = {
	        "IMAGES_STORE": images_store,
	        "MYSQL_HOST": host,
	        "MYSQL_USER": username,
	        "MYSQL_PASSWORD": pasword,
	        "MYSQL_PORT": port,
	        "MYSQL_DBNAME": db_name,
	        "MYSQL_CHARSET": 'utf8'
	    }
	    super(JdSpider, self).__init__()  # 初始化父类放在最后,才能初始化父类的类变量
        ...
2. 使用指定settings创建crawl

首先要知道如何使用脚本启动爬虫,scrapy提供了两种方式CrawlerProcess和CrawlerRunner,这里以CrawlerProcess为例:

crawl = CrawlerProcess(get_project_settings())
crawl.crawl(myspider)
crawl.start()

get_project_settings()获取的即为全局的设置,所以可以通过创建不同settings,或者修改全局的settings来启动爬虫

# 1. 创建settings
settings = Settings()
settings.set("ITEM_PIPELINES" , {
    'pipelines.DuplicatesPipeline': 200,
    'pipelines.IpProxyPoolPipeline': 300,
})
settings.set("DEFAULT_REQUEST_HEADERS",{
  'Accept': 'text/html, application/xhtml+xml, application/xml',
  'Accept-Language': 'zh-CN,zh;q=0.8'}
)
settings.set("DOWNLOADER_MIDDLEWARES",{
   'useragent_middlewares.UserAgent': 1,
   'proxy_middlewares.ProxyMiddleware':100,
   'scrapy.downloadermiddleware.useragent.UserAgentMiddleware' : None,
}
)
settings.set("DOWNLOAD_DELAY",1)
settings.set("COOKIES_ENABLED",False)
settings.set("ROBOTSTXT_OBEY",True)

# 2. 或修改项目settings
settings = get_project_settings()
settings.set('IMAGES_STORE', 'D:/images3')

crawl = CrawlerProcess(settings)
crawl.crawl(myspider)
crawl.start()

2. UI主线程中开启子线程启动scrapy爬虫

scrapy一般用命令启动,在程序中,可以利用cmdline执行对应的命令来启动,可以利用scrapy内置的crawler类来启动(但由于在子线程中启动会有一些bug)。

开启一个子线程执行爬虫,并不断传递信号给主线程,来实时获取信息。但scrapy是一个独立的框架,无法把子线程中定义的信号,放到scrapy模块中去发送。
而scrapy框架自带的信号,也无法传递到scrapy外面?
所以需要使用一些其他的方法,来获取爬虫的运行情况。
一个是将信息写入文件中,从文件中获取,但必须在爬虫结束之后,才获取,另一个方法是开一个进程执行scrapy,并获取进程标准输出。

		spider_cmdline = "scrapy crawl {} -a name={} --logfile=log.log".format(self.spider_name, self.keyword)
        p = subprocess.Popen(spider_cmdline, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
        self.message_singal.emit(' 爬虫已启动')
        # 获取返回结果(百分比,和当前下载记录),信号返回两个变量
        while True:
            if subprocess.Popen.poll(p) is None:  # 结束为-1,包含输出为空
                data = p.stdout.readline().decode('utf-8').strip()
                if data == 'close':
                    self.message_singal.emit(data)  # 发送内容
                else:
                    print("data: ", data)
                    if data.isdigit():
                        self.percent_signal.emit(int(data))  # 发送进度信号

            else:
                # print("爬虫结束")
                # 杀死子进程
                self.message_singal.emit('爬虫完成,可关闭该窗口,进行其他爬虫任务')
                p.terminate()
                p.kill()
                break
            time.sleep(2)

另外:

PYQT5开启多个线程和窗口,多线程与多窗口的交互
PyQt5和Scrapy开发可视化爬虫(二)

你可能感兴趣的:(python)