python爬虫基于Selenium的股票信息爬取工具实现
基于Selenium的股票信息爬取工具的实现
该项目旨在爬取可转债及其对应正股实时变化的行情信息,包括股票及债券的代码,名称,最新价,涨跌幅等,并通过pyqt5进行GUI设计,以达到可视化的目的
一.背景介绍
1.什么是可转换债券
可转换债券(Convertible bond )是债券持有人可按照发行时约定的价格将债券转换成公司的普通股票的债券。
如果债券持有人不想转换,则可以继续持有债券,直到偿还期满时收取本金和利息,或者在流通市场出售变现。如果持有人看好发债公司股票增值潜力,在宽限期之后可以行使转换权,按照预定转换价格将债券转换成为股票,发债公司不得拒绝。该债券利率一般低于普通公司的债券利率,企业发行可转换债券可以降低筹资成本。
2.为什么要研究可转债对应的正股
可转债的走势一般和对应正股的走势具有很强的相关性。但可转债的走势有可能稍稍滞后于正股,所以可以筛选对应正股在短时间内快速增长的可转债,存在可能的获利机会。
二.技术选型
1.IDE:Pycharm2020.1
2.Language: Python
3.数据来源:东方财富网>行情中心>债券市场>可转债比价表
4.WEB自动化工具:Selenium
Selenium是一个用于测试网站的自动化测试工具,它可以代替人进行鼠标点击,在搜索框中输入内容等操作。支持各种浏览器包括Chrome、Firefox、Safari等主流界面浏览器,同时也支持phantomJS*面浏览器。
本项目选用Selenium主要出于以下两点考虑:
(1)股票行情信息为实时动态数据,在后台由JS渲染得到,用常规的Xpath和css选择器等方法无法获取到想要的数据。而使用Selenium的好处是,浏览器能请求到的,Selenium也能请求到,爬虫功能更强大。
(2)分析我们要爬取的页面发现,如果我们点击“下一页”按钮到下一个界面,对应的URL并没有发生改变,这就意味着通过在代码中通过改变URL进行翻页是不现实的,也就不得不使用Selenium获取到对应按钮的位置并调用click()方法进行点击来实现。
需要注意的是,Selenium3.x调用浏览器必须有一个webdriver驱动文件,不同浏览器的不同版本都对应相应的webdriver,并且进行环境变量的配置,有关Selenium的具体内容,可参考这篇博文:Python Selenium库的使用
5.GUI设计:Pyqt5
Qt Designer中自带的许多类似按钮,显示框等组件,其拖拽式设计模式用起来也十分方便,信号与槽机制理解起来也比较容易,是新手入门GUI设计的很棒的工具。其原理和用法和C++ QT异曲同工,有相关基础的同学上手会很快,Pyqt5入门可参考:PyQt5(designer)入门教程
有一点值得单独拿出来强调一下:在designer中设计好界面后保存成.ui为后缀的文件,之后需要通过在cmd中先cd进入文件保存的目录下,再输入一条指令将其转为.py为后缀的文件,这样就会发现目录中多出一个.py文件。
pyuic5 -o name.py name.ui #实际情况将‘name’替换为自己的文件名即可
此时尝试运行刚刚生成的.py文件是没用的,因为生成的文件并没有程序入口。因此我们在同一个目录下另外创建一个程序叫做“main.py”,并输入如下内容,其中gui_file_name就是.py后缀的文件名,需自行替换。
import sys from PyQt5.QtWidgets import QApplication, QMainWindow import gui_file_name if __name__ == '__main__': app = QApplication(sys.argv) MainWindow = QMainWindow() ui = gui_file_name.Ui_MainWindow() ui.setupUi(MainWindow) MainWindow.show() sys.exit(app.exec_())
6.其他:多线程
最开始博主将主业务代码和控制UI的代码放在了一起,结果出现了主窗口未响应的情况。后经查阅资料及分析得知, Qt 中所有界面都是在 UI 线程中(也被称为主线程,就是执行了QApplication::exec()的线程),在这个线程中执行耗时的操作(比如爬取数据的进程),就会阻塞 UI 线程,从而让界面停止响应。界面停止响应,用户体验自然不好,不过更严重的是,有些窗口管理程序会检测到你的程序已经失去响应,可能会建议用户强制停止程序,这样一来你的程序可能就此终止,任务再也无法完成。所以,为了避免这一问题,我们要使用 QThread 开启一个新的线程。关于QThread模块的使用,有比较成熟的模板,套路都是固定的,把主业务逻辑扔在run()函数中,再注意下开的这个线程与UI界面的数据交互即可。具体可参考这两篇博文:PyQt5 中的多线程的使用(上) PyQt5 中的多线程的使用(下)
三.代码详析
下面粘出“main.py”的完整代码,并通过行末注释对关键步骤进行解析:
import sys import zhuanzhai import re from selenium import webdriver from scrapy.selector import Selector from PyQt5.QtWidgets import QApplication, QMainWindow from PyQt5.QtCore import pyqtSignal, QThread, QTimer
timer = QTimer() selected_bond_code = [] selected_bond_name = [] selected_bond_price = [] selected_bond_rate = [] selected_stock_code = [] selected_stock_name = [] selected_stock_price = [] selected_stock_rate = [] j = 0 class MyWindows(QMainWindow, zhuanzhai.Ui_MainWindow): def __init__(self): super(MyWindows, self).__init__() self.setupUi(self) self.thread = WorkThread() self.pushButton.clicked.connect(self.select_func)#点击按钮触发select_func函数进行股票的筛选 self.thread.signal.connect(self.Printf)#筛选进程结束调用Printf函数将所得数据输出 # 进行股票筛选 def select_func(self): self.textBrowser.clear()#每次点击‘筛选’按钮后,将TextBrowser原有的内容清空 self.thread.data = self.lineEdit.text()#从UI界面获取lineEdit中用户输入的涨幅 self.thread.start() # 启动线程 #向TextBrowser输出数据 def Printf(self, selected_bond_code): global j#将j定义为全局变量,再次调用printf函数时,j从原值继续递增 while j < len(selected_bond_code): self.textBrowser.append("转债代码:"+selected_bond_code[j])#利用append方法而不是setText,后者会覆盖原有数据,而前者不会 self.textBrowser.append("转债名称:"+selected_bond_name[j]) self.textBrowser.append("最新价:"+selected_bond_price[j]) self.textBrowser.append("涨跌幅:"+selected_bond_rate[j]) self.textBrowser.append("正股代码:"+selected_stock_code[j]) self.textBrowser.append("正股名称:"+selected_stock_name[j]) self.textBrowser.append("最新价:"+selected_stock_price[j]) self.textBrowser.append("涨跌幅:"+selected_stock_rate[j]) self.textBrowser.append("---------------------------------") j = j+1 class WorkThread(QThread): signal = pyqtSignal(list) # 括号里填写信号传递的参数类型,本例中为获取到的转债代码列表 def __init__(self): super(WorkThread, self).__init__() def run(self): browser = webdriver.Chrome(executable_path="D:/Anaconda/chromedriver.exe")#获取本地Chromedriver的存储路径 browser.get("http://quote.eastmoney.com/center/fullscreenlist.html#convertible_comparison")#需爬取界面的网址 t_selector = Selector(text=browser.page_source) all_page = t_selector.xpath('//*[@id="common_table_paginate"]/span[1]/a[5]/text()').extract()[0]#获取总页数,为之后翻页遍历所有页提供终止条件 count = 0 while count < int(all_page): t_selector = Selector(text=browser.page_source) bond_code = t_selector.css("td:nth-child(2) a::text").extract()#通过css选择器获取所需内容并存入中转列表中 bond_name = t_selector.css("td:nth-child(3) a::text").extract() bond_price = t_selector.css("td:nth-child(4) span::text").extract() bond_rate = t_selector.css("td:nth-child(5) span::text").extract() stock_code = t_selector.css("td:nth-child(7) a::text").extract() stock_name = t_selector.css("td:nth-child(8) a::text").extract() stock_price = t_selector.css("td:nth-child(9) span::text").extract() stock_rate = t_selector.css("td:nth-child(10) span::text").extract() i = 0 while i < len(stock_rate): re_stock_rate = re.match(r"(.*)%", stock_rate[i])#利用正则表达式进行匹配,语法re.match(r"正则表达式",需匹配字符串) if (float(re_stock_rate.group(1)) > float(self.data)):#将正股涨幅与用户输入的预期涨幅相比较,若大于这个值,则将相关信息存入对应的结果列表中 selected_bond_code.append(bond_code[i]) selected_bond_name.append(bond_name[i]) selected_bond_price.append(bond_price[i]) selected_bond_rate.append(bond_rate[i]) selected_stock_code.append(stock_code[i]) selected_stock_name.append(stock_name[i]) selected_stock_price.append(stock_price[i]) selected_stock_rate.append(stock_rate[i]) i += 1 self.signal.emit(selected_bond_code)#向UI进程传递结果 browser.find_element_by_css_selector('.next').click()#获取下一页并点击 count += 1 self.sleep(1) if __name__ == '__main__':#程序入口 app = QApplication(sys.argv) mywindow = MyWindows() mywindow.show() sys.exit(app.exec_())
另外需要注意环境的配置,安装好所需的库,有关Pycharm虚拟环境的创建与配置,可参考:Pycharm虚拟环境(Virtualenv)配置
四.效果展示
程序运行效果图:
总计300多条数据,爬取速度大约在10s左右。支持修改输入框中的涨幅限制,再次点击按钮即可进行下次筛选,点击右上角叉号结束进程退出程序。
本文地址:https://blog.csdn.net/qq_45759963/article/details/108248163
上一篇: 数据结构与就算法(选择排序)