爬虫工具(tkinter+scrapy+pyinstaller)

需求介绍输入:关键字文件,每一行数据为一爬取单元。若一行存在多个and关系的关键字 ,则用|隔开处理:爬取访问6个网站的推送,获取推送内容的标题,发布时间,来源,正文第一段(不是图片或者图例)输出:输出到csv文件ui:窗口小程序,能实时地跟踪爬虫进度运行要求:不依赖于python环境,独立运行的exe文件

分析实现的主要程序

最后pyinstaller 打包crawl.py即可

实现

uI中的线程控制

import tkinter as tk
import time
import sys
import queue
import threading
def fmtTime(timestamp):
    localtime=time.localtime(timestamp)
    datetime=time.strftime("%Y-%m-%d %H:%M:%S",localtime)
    return datetime


class re_Text():
    def __init__(self,queue):
        self.q=queue
    def write(self,content):
        self.q.put(content)


class GUI(object):
    def __init__(self,root):
        self.root=root
        self.q=queue.Queue()
        self.initGUI(root)

    def show_msg(self):
        if not self.q.empty():
            self.text.insert("insert",self.q.get())
            self.text.see(tk.END)
        self.root.after(100,self.show_msg)
        
    def initGUI(self,root):
        root.title("点击数据")
        root.geometry('400x200+700+500')
        bn=tk.Button(root,text="click",width=10,command=self.show)
        #pack 控制排版
        bn.pack(side="top")
        scrollBar = tk.Scrollbar(root)
        scrollBar.pack(side="right", fill="y")
        self.text = tk.Text(root, height=10, width=45, yscrollcommand=scrollBar.set)
        self.text.pack(side="top", fill=tk.BOTH, padx=10, pady=10)
        #动态绑定
        scrollBar.config(command=self.text.yview)
        #不要想着中断机制或者调用子函数机制把它视为另一个线程
        # (write通信作用)
        sys.stdout=re_Text(self.q)
        root.after(100,self.show_msg)
        root.mainloop()
        
    def _show(self):
        i = 0
        for i in range(4):
            # 顺序执行 ui的刷新线程没有抢占cpu阻塞在这  等过了3秒后才刷新到text
            time.sleep(1)
            # 重定向 调用write
            print(fmtTime(time.time()))
            
    def show(self):
        # 创建子线程 窗口程序可以不断地监听
        T=threading.Thread(target=self._show)
        T.start()
        
if __name__=="__main__":
    root=tk.Tk()
    GUI(root)

scrapy.py

技术细节可以参考之前的文章这里就直接写spider了

import scrapy
from scrapy import Selector
from scrapy import Request, signals
import pandas as pd
import re
from x93.items import csvItem
import os
import sys

class ExampleSpider(scrapy.Spider):
    name = 'spider'

    def __init__(self, **kwargs):
        super(ExampleSpider, self).__init__(**kwargs)
        self.data = list()
        self.keyws=kwargs.get('keywords')
        #print(self.keyws)
        print('----------')
        self.sites=[
            'sh93.gov.cn',
            '93.gov.cn',
            'shszx.gov.cn',
            'tzb.ecnu.edu.cn'
            'ecnu.edu.cn/info/1094',
            'ecnu.edu.cn/info/1095'
        ]
    def start_requests(self):
        #keyw=self.keyws
        for keyw in self.keyws:
            keyw=keyw.strip()
            keyw=keyw.split('|')
            keyw="+".join(keyw)
            for site in self.sites:
                self.logger.info("site"+site)
                #url=f'https://cn.bing.com/search?q=site%3a{site}+allintext%3a{keyw}&first=1'
                url = f'https://cn.bing.com/search?q=site%3a{site}+{keyw}&first=1'
                yield Request(url, callback=self.parse, cb_kwargs={'first':1,'site':site,'keyw':keyw,'totallist':0})
    def parse(self, response,**kwargs):
    #百度网页 列表内容
        res=Selector(text=response.text)
        for a in res.xpath('//h2/a[@target="_blank"]'):
            title = a.xpath('./text()').get()
            href = a.xpath('./@href') .get()
            out = re.search("index",href)
            htm= re.search("htm",href)
            # 排除含index列表页 json 数据页
            if out!=None or htm==None:
                continue
            kwargs['href']=href
            yield Request(href,callback=self.get_detail,cb_kwargs=kwargs)
        #翻页
        # if kwargs['first']==1:
        #     nub=res.xpath(r'//span[@class="sb_count"]/text()').get()
        #     nub="".join(re.findall(re.compile('[\d]+'),nub))
        #     kwargs['totallist']=int(nub)
        #     #self.logger.info("kwargs['totallist']" + kwargs['totallist'])
        # if  kwargs['first']+10<kwargs['totallist']:
        #     self.logger.info(f"kwargs['totallist']{kwargs['totallist']}")
        #     kwargs['first'] =kwargs['first'] + 10
        #     url=f'https://cn.bing.com/search?q=site%3a{kwargs["site"]}+allintext%3a{kwargs["keyw"]}&first={kwargs["first"]} '
        #     self.logger.info(f"url{url}")
        #     yield Request(url, callback=self.parse, cb_kwargs=kwargs)

    def get_detail(self,response,**kwargs):
        res = Selector(response)
        title=''
        date =''
        content=''
        source=''
        if kwargs['site']=='sh93.gov.cn':
            try:
                title = res.xpath('//h3[contains(@class,"article-title")]/text()').get()
                date = res.xpath('//div[contains(@class,"article-title-news")]/span/text()').get()
                date = "".join(re.findall(re.compile(r"[\d-]+"), date))
                try:
                    source=res.xpath('//span[@class="ml20"]/text()').get()
                except TypeError:
                    source="九三上海市委新闻"
                try:
                    content=res.xpath('//div[contains(@class,"article-content")]/p[not (@style="text-align: center;")]/'
                                      'text()').get().strip()
                except :
                    content=res.xpath('//span[contains(@style,"font-family")]/text()').get().strip()
            except:
                try:
                    title = res.xpath('//td[@class="pix16blackl32"]/text()').get()
                    date = res.xpath('//td[@class="pixh4_line24"]/text()').get()
                    date = re.findall(re.compile(r"[\d-]+"), date)
                    date = "-".join(date)
                    source = "九三学社上海市委员会"
                    content = res.xpath("//p/text()").get()
                except:
                    self.logger.error(f"无法解析{kwargs['href']}")
        if kwargs['site']=='93.gov.cn':
            title=res.xpath('//div[contains(@class,"pageTitle")]/h2/text()').get()
            date=res.xpath('//div[contains(@class,"pageTitle")]//ul/li[1]/text()').get()
            date = "".join(re.findall(re.compile(r"[\d-]+"), date))
            source=res.xpath('//div[contains(@class,"pageTitle")]//ul/li[2]/text()').get()[3:]
            try:
                content = res.xpath('//div[@class="text"]/p[not (@style="text-align: center;")]/text()').get().strip()
            except AttributeError:
                #print("url:"+kwargs['href'])
                content= res.xpath('//div[@class="text"]//span[contains(@style,"font-family")]/text()').get().strip()
                #content= res.xpath
        if kwargs['site']=='shszx.gov.cn':
            title=res.xpath('//h2[@id="ivs_title"]/text()').get()
            date= res.xpath('//div[@class="fc con22 lh28 grey12"]/text()').get()
            date="".join(re.findall(re.compile(r"[\d-]+"), date))
            source="上海政协"
            cnt=1
            while content == '':
                cnt=cnt+1
                content=res.xpath(f'//div[@id="ivs_content"]/p[not (@align="center") and '
                                  f'not (@style="text-align: center;")][{cnt}]/text()').get().strip()
        if kwargs['site']=='tzb.ecnu.edu.cn':
            title=res.xpath('//h1[@class="arti_title"]/text()').get()
            #text() 会取第一个
            date=res.xpath('//span[@class="arti_update"]/text()').get()
            date="".join(re.findall(re.compile(r"[\d-]+"), date))
            source=res.xpath('//span[@class="arti_department"]/text()').get()[4:]
            content=res.xapth('//div[@class="wp_articlecontent"]//p[contains(@style,"font-size")]/text()').get().strip()
        if 'ecnu.edu.cn' in kwargs['site']:
            title=res.xpath('//h2[@class="m3nTitle"]/text()').get()
            date=res.xpath('//span[@class="m3ntm"]/text()').get()
            date=re.findall(re.compile(r"[\d-]+"), date)
            date="-".join(date)
            if "1094" in kwargs['site']:
                source="华东师范大学新闻热点"
            else:
                source = "华东师范大学媒体关注"
            content=res.xpath('//p/span[contains(@style,"font-family")]//text()|'
                              '//p[contains(@style,"text-align:justify")]/text()').get().strip()
        item=csvItem()
        item['keyword']=kwargs['keyw']
        item['title']=title
        item['date']=date
        item['source']=source
        item['content']=content
        item['url']=kwargs['href']
        print(title, date, content)
        yield item

ui脚本中运行scrapyscrapy 脚本运行有三种方式,实现细节可以参考官方文档cmdline.execute方式只能运行一个爬虫,而其他两种方式可以同时运行多个(异步+异步)。

scrapy爬虫比较耗时,需要放在子线程工作,因此选用crawlRunner(crawlProcess要求运行在主线程就不行),但是不清楚为什么刚开始正常运行后来又是报错提示,signal run in main thread.可以尝试的解决方法,参考博客

# !/user/bin/env Python3
# -*- coding:utf-8 -*-

from scrapy import *
import scrapy
from scrapy import Selector
from scrapy import Request, signals
import pandas as pd
import re
import time
import tkinter as tk
from tkinter import filedialog, dialog
import os
import threading
import logging
import sys
from scrapy.crawler import CrawlerProcess
from scrapy.utils.project import get_project_settings
from scrapy.cmdline import execute
from twisted.internet import reactor
from scrapy.crawler import CrawlerRunner
from scrapy.utils.log import configure_logging
from x93.spiders.spider import ExampleSpider
from scrapy.utils.project import get_project_settings

logger = logging.getLogger(__name__)
file_path = ''
file_text = ''
class re_Text():
    def __init__(self,text):
        self.text=text
    def write(self,content):
        self.text.insert("insert",content )
        self.text.see(tk.END)
class GUI():
    def __init__(self):
        self.root=tk.Tk()
        self.root.title("ecnu数据爬取工具")
        self.root.geometry("400x400+200+200")
    def initGUI(self):
        # self.root.title('窗口标题')  # 标题
        # self.root.geometry('500x500')  # 窗口尺寸
        self.scrollBar = tk.Scrollbar(self.root)
        self.scrollBar.pack(side="right", fill="y")
        self.text = tk.Text(self.root, height=10, width=45, yscrollcommand=self.scrollBar.set)
        self.text.pack(side="top", fill=tk.BOTH, padx=10, pady=10)
        #self.scrollBar.config(command=self.text.yview)  # 动态绑定 滚动条随着鼠标移动
        bt1 = tk.Button(self.root, text='打开文件', width=15, height=2, command=self.open_file)
        bt1.pack()
        bt2 = tk.Button(self.root, text='爬取数据', width=15, height=2, command=self._app)
        bt2.pack()
        #bt3 = tk.Button(self.root, text='保存文件', width=15, height=2, command=save_file)
        #bt3.pack()

    def open_file(self):
        '''
        打开文件
        :return:
        '''
        global file_path
        global file_text
        file_path = filedialog.askopenfilename(title=u'选择文件', initialdir=(os.path.expanduser('H:/')))
        print('打开文件:', file_path)
        if file_path is not None:
            with open(file=file_path, mode='r+', encoding='utf-8') as file:
                file_text = file.readlines()
                print(file_text)

    def thread_it(func, *args):
        '''将函数打包进线程'''
        # 创建
        t = threading.Thread(target=func, args=args)
        # 守护 !!!
        t.setDaemon(True)
        # 启动
        t.start()
        # 阻塞--卡死界面!
        #t.join()
    def _app(self):
        t=threading.Thread(target=self.app,args=())
        t.start()
    def app(self):
        global file_text

        logger.info(f"type(file_text){type(file_text)}")
        runner = CrawlerRunner(get_project_settings())
        d = runner.crawl(ExampleSpider,keywords=file_text)
        d.addBoth(lambda _: reactor.stop())
        reactor.run()
        self.text.insert('insert', "爬取成功")
        # process = CrawlerProcess(get_project_settings())
        # process.crawl(ExampleSpider,keywords=file_text)
        # process.start()
        # cmd=f'scrapy crawl spider -a kw={file_text}'.split()
        # execute(cmd)
        # cmd = 'python Run.py'
        # print(os.system(cmd))
        
g=GUI()
g.initGUI()
sys.stdout=re_Text(g.text)
g.root.mainloop()  # 显示

pyinstaller 打包因为看到很多博客都说用pyinstaller打包scrapy 需要引入较多依赖,修改配置文件等多余的操作,一直没敢下手尝试直接用pyinstaller打包主程序,转向成功案例较多的单线程运行爬虫方式,后来还是败给的scrapy框架,返回失败的retry机制以及代理更换的便捷,毕竟批量输入无可避免的有无法访问目标计算机问题。

打包后的程序(调试问题后就上传占个位)

文章转载自:mingruqi

原文链接:https://www.cnblogs.com/Im-Victor/p/17081823.html

体验地址:引迈 - JNPF快速开发平台_低代码开发平台_零代码开发平台_流程设计器_表单引擎_工作流引擎_软件架构

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/291145.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

自动化测试框架总结

1. 单元测试框架 几乎所有的主流语言&#xff0c;都会有其对应的单元测试框架,下面简单介绍一下python,java,C#三种语言的常见单元测试框架 1.1 Python python常见单元测试框架包括unittest, pytest 1.1.1 unittest unittest单元测试框架不仅可以适用于单元测试&#xff0c…

Windows重装升级Win11系统后 恢复Mysql数据

背景 因为之前电脑硬盘出现问题&#xff0c;换了盘重装了系统&#xff0c;项目的数据库全部没了&#xff0c;还好之前的Mysql是安装在的D盘里&#xff0c;还有留存文件 解决办法 1.设置环境变量 我的路径是 D:\SoftWare\Application\mysql-5.7.35-winx64 此电脑右键属性 …

auto关键字的含义以及常见用法,C++11中的关键字

一、auto关键字的含义&#xff1a; auto&#xff1a;这是 C11 引入的关键字&#xff0c;用于自动推断变量的类型&#xff1b; 二、auto关键字的常见用法&#xff1a; auto 关键字在 C 中用于自动推断变量的类型&#xff0c;它可以让编译器根据初始化表达式的类型推导出变量的…

Python 介绍和环境准备

一、概述 Python 是一个高层次的结合了解释性、编译性、互动性和面向对象的解释性编程语言。 Python 是一种解释型语言&#xff1a; 这意味着开发过程中没有了编译这个环节。类似于PHP和Perl语言。 Python 是交互式语言&#xff1a; 这意味着&#xff0c;您可以在一个 Python…

和鲸解放军总医院连续生理数据分析引擎入选爱分析数据智能最佳实践案例

近日&#xff0c;“2023 爱分析 数据智能最佳实践案例”评选活动落下帷幕&#xff0c;和鲸科技基于旗下数据科学协同平台 ModelWhale 携手解放军总医院联合打造的《解放军总医院连续生理数据分析引擎》成功入选&#xff0c;有力证明了该案例于数据资产归集、数据架构升级、数据…

UE5.1保存资源报错

UE5.1保存资源报错 错误&#xff1a; The asset /Game/XXX(XXX.uasset) failed to save. Cancel: Stop saving all assets and return to the editor. Retry: Attempt to save the asset again. Continue: Skip saving this asset only. 解决: 1. 可能是进程中有多开的项目&…

iOS 组件开发教程——手把手轻松实现灵动岛

1、先在项目里创建一个Widget Target 2、一定要勾选 Include live Activity&#xff0c;然后输入名称&#xff0c;点击完成既可。 3、在 Info.plist 文件中声明开启&#xff0c;打开 Info.plist 文件添加 NSSupportsLiveActivities&#xff0c;并将其布尔值设置为 YES。 4、我…

Spark内核解析-脚本解析2(六)

2、脚本解析 在看源码之前&#xff0c;我们一般会看相关脚本了解其初始化信息以及Bootstrap类&#xff0c;Spark也不例外&#xff0c;而Spark中相关的脚本如下&#xff1a; %SPARK_HOME%/sbin/start-master.sh %SPARK_HOME%/sbin/start-slaves.sh %SPARK_HOME%/sbin/start-all…

freeRTOS——事件标志组知识总结及实战

1事件标志组概念 事件标志组&#xff1a;是一组事件标志位的集合&#xff0c; 可以简单的理解事件标志组&#xff0c;就是一个整数。 其特点&#xff1a; 1&#xff09;它的每一个位表示一个事件&#xff08;高8位不算&#xff09; 2&#xff09;每一位事件的含义&#xff0c;…

Spark内核解析-节点启动4(六)

1、Master节点启动 Master作为Endpoint的具体实例&#xff0c;下面我们介绍一下Master启动以及OnStart指令后的相关工作 1.1脚本概览 下面是一个举例&#xff1a; /opt/jdk1.7.0_79/bin/java -cp /opt/spark-2.1.0/conf/:/opt/spark-2.1.0/jars/*:/opt/hadoop-2.6.4/etc/ha…

UI5与后端的文件交互(四)

文章目录 前言一、后端开发1. 新建管理模板表格2. 新建Function&#xff0c;动态创建文档 二、修改UI5项目1.Table里添加下载证明列2. 实现onClickDown事件 三、测试四、附 前言 这系列文章详细记录在Fiori应用中如何在前端和后端之间使用文件进行交互。 这篇的主要内容有&…

2008年全国生态自然地域划分数据,shp格式,来源于国家生态环境部发布的《全国生态功能区》2008年版

数据名称: 全国生态自然地域划分数据 数据格式: Shp 数据时间: 2008年 数据几何类型: 面 数据坐标系: WGS84 数据来源&#xff1a;国家生态环境部发布的《全国生态功能区》2008年版 数据字段&#xff1a; 序号字段名称字段说明1bh编号2stq_1生态区_大类3stq_2生态区…

Spring Boot 完善订单【五】集成接入支付宝沙箱支付

1.1.什么是沙箱支付 支付宝沙箱支付&#xff08;Alipay Sandbox Payment&#xff09;是支付宝提供的一个模拟支付环境&#xff0c;用于开发和测试支付宝支付功能的开发者工具。在真实的支付宝环境中进行支付开发和测试可能涉及真实资金和真实用户账户&#xff0c;而沙箱环境则提…

网络对讲终端 网络音频终端 网络广播终端SV-7011V使用说明

高速路sip广播对讲求助 隧道sip对讲调度SIP-7011 网络广播终端SV-7011 壁挂式对讲终端网络监听终端SIP广播终端 sip语音对讲终端SIP-7011 SV-7011网络对讲终端网络对讲、网络厂播、监听 SV-7101网络解码终端提供一路线路输出接功放或有源音箱。 SV-7102网络解码广播终端两…

OpenGL如何基于glfw库 进行 点线面 已解决

GLFW是现在较流行、使用广泛的OpenGL的界面库&#xff0c;而glut库已经比较老了。GLEW是和管理OpenGL函数指针有关的库&#xff0c;因为OpenGL只是一个标准/规范&#xff0c;具体的实现是由驱动开发商针对特定显卡实现的。由于OpenGL驱动版本众多&#xff0c;它大多数函数的位置…

一加 Buds 3正式发布:普及旗舰音质 一加用户首选

1月4日&#xff0c;一加新品发布会正式推出旗下新款耳机一加 Buds 3。延续一加经典美学&#xff0c;秉承音质完美主义追求&#xff0c;一加 Buds 3全面普及一加旗舰耳机体验&#xff0c;其搭载旗舰同款“超清晰同轴双单元”&#xff0c;配备49dB 4000Hz超宽频主动降噪&#xff…

企语iFair 协同管理系统 任意文件读取漏洞复现(CVE-2023-47473)

0x01 产品简介 企语iFair协同管理系统是一款专业的协同办公软件,该管理系统兼容性强,适合多种企业类型。该软件永久免费,绿色安全,无需收取费用即可使用所有功能。企语iFair协同管理系统同时兼容了Linux、Windows两种操作系统 0x02 漏洞概述 企语iFair协同管理系统getup…

LangChain与昇腾

LangChain这个词今年已经听烂了&#xff0c;今天基于昇腾的角度总结一下&#xff1a; Why LangChain &#xff1f; 场景&#xff1a;构建一个LLM应用 在构建一个新项目时&#xff0c;可能会遇到许多API接口、数据格式和工具。要去研究每一个工具、接口很麻烦。 假设要构建一…

Flume基础知识(三):Flume 实战监控端口数据官方案例

1. 监控端口数据官方案例 1&#xff09;案例需求&#xff1a; 使用 Flume 监听一个端口&#xff0c;收集该端口数据&#xff0c;并打印到控制台。 2&#xff09;需求分析&#xff1a; 3&#xff09;实现步骤&#xff1a; &#xff08;1&#xff09;安装 netcat 工具 sudo yum …

java数据结构与算法刷题-----LeetCode70. 爬楼梯

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 很多人觉得动态规划很难&#xff0c;但它就是固定套路而已。其实动态规划只…
最新文章