吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 14713|回复: 62
收起左侧

[Python 转载] 入Python第一步,从贴“python爬取基金 筛选股票 ”

  [复制链接]
zxl4689 发表于 2020-1-13 10:18
如果帖子存在违规,麻烦删除,谢谢。前言:
     本人先前没有接触python,所以以下所写代码如果有误,请各位大大不吝赐教,万分感谢。
     打算学习python是因为一位论坛贴“股价翻番 人生赢家,python爬取基金 筛选股票”,传送门:https://www.52pojie.cn/thread-1086853-1-1.html 。看了这个帖子,发现python对于网页的处理好方便啊,如果换成VC来搞,需要自己处理的地方太多太多了。以前总是看别人爬虫,这个帖子写的也很详细,果断入手python,小白一枚,初次练习,如有错误,还望指正。
正文:
     原贴中获取基金的持仓数据,再此说明一下,获取的持仓数据并非实时,如果有炒股的看到,老股民也就罢了,新股民慎用提取出来的数据,慎用!慎用!慎用!
     优化思路:
     1、原代码支持提取了基金的持仓股票的名称。
           优化增加股票的持仓量数据,可以练习提取数据操作。
     2、原代码是单线程
           效率太低,要跑好几个小时。实现多线程处理,优化速度。
     3、原代码打印执行进度无法知道采集进度
          增加进度条显示

     实现1:
     
[Python] 纯文本查看 复制代码
# selenium请求[基金持仓股票详情页面url]的方法,爬取基金的持仓股票名称、持仓量;
def hold_a_position(url):
    stock_name = []  # 定义一个数组,存储证券的名称
    amount = [] #定义一个数组,存储证券的持仓

    # 浏览器动作
    chrome_options = webdriver.ChromeOptions()
    chrome_options.add_argument('--headless')
    chrome_options.add_argument('--disable-gpu')
    driver = webdriver.Chrome(options=chrome_options)  # 初始化浏览器,无浏览器界面的,保持后台运行;

    driver.get(url)  # 请求基金持仓的信息
    element_result = is_element(driver, "tol")  # 是否存在这个元素,用于判断是否有持仓信息;
    #print(url + '\n')
    if element_result == True:  # 如果有持仓信息则爬取;
        wait = WebDriverWait(driver, 3)  # 设置一个等待时间
        input = wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'tol')))  # 等待这个class的出现;
        ccmx_page = driver.page_source  # 获取页面的源码
        ccmx_xpath = etree.HTML(ccmx_page)  # 转换成成 xpath 格式
        trs = ccmx_xpath.xpath("//div[@class='txt_cont']//div[@id='cctable']//div[@class='box'][1]//tr")
        if trs:
            #print("开始获取基金持仓信息")
            for tr in trs:
                stock_name_t = tr.xpath("./td[3]//text()")
                if len(stock_name_t) != 0:  #获取成功才返回数据
                    stock_amount_t = tr.xpath("./td[8]//text()")
                    if len(stock_amount_t) != 0:
                        stock_amount = stock_amount_t[0].replace(",", "")   #去除持仓中的逗号
                        if stock_amount.split(".")[-1].isdigit():   #如果是数值则继续,此处还待优化,仍有字母数据返回
                            stock_name.append(stock_name_t[0])
                            amount.append(stock_amount)
            #print(stock_name)
            #print(amount)
            driver.quit()
            return stock_name,amount
    else:  # 如果没有持仓信息,则返回null字符;
        driver.quit()
        return "null","null"

     原代码中获取股票名称是:ccmx_xpath.xpath("//div[@class='txt_cont']//div[@id='cctable']//div[@class='box'][1]//td[3]//text()")
     找了xpath的说明,还是不知道一行代码怎么获取股票名称和持仓量,只能本办法分开获取,写入两个数组了。
     代码执行过程中发现12g的内存被吃满了,对于我的办公机太严苛了,看进程,发现chromedriver为什么那么多,果断取消执行,度娘后发现原代码的driver.Close()引起的,改为driver.quit(),重新执行后正常。资料:https://blog.csdn.net/yangfengjueqi/article/details/84338167
     实现2:
     
[Python] 纯文本查看 复制代码
if __name__ == '__main__':
    fund_url, fund_name = page_url()  # 获取首页数据,返回基金url的数组和基金名称的数组;

    if len(fund_url) == len(fund_name):  # 判断获取的基金url和基金名称数量是否一致
        threadNum = 10  #线程数量
        all_count=len(fund_url)    #基金数量
        threadId = 1    #线程ID
        threads = []    #线程集合

        #开启多线程
        for tName in range(threadNum):
            thread = myThread(threadId,("Thread-%d" % tName),fund_url, fund_name, round(all_count*(threadId-1)/threadNum+1), round(all_count*threadId/threadNum))
            thread.start()
            threads.append(thread)
            threadId += 1

        #显示进度条
        while nPos<all_count:
            progress_bar(nPos,all_count)
            time.sleep(5)

        # 等待所有线程完成
        for t in threads:
            t.join()
        print("退出主线程")

        #保险起见,关闭所有的chrome进程
        os.system('taskkill /im chromedriver.exe /F')
        os.system('taskkill /im chrome.exe /F')

    else:
        print("基金url和基金name数组数量不一致,退出。")
        exit()

     
[Python] 纯文本查看 复制代码
#多线程class myThread (threading.Thread):
    def __init__(self, threadID, name,fund_url, fund_name, begin_num, end_num):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.fund_url = fund_url
        self.fund_name = fund_name
        self.begin_num = begin_num
        self.end_num = end_num
    def run(self):
        print ("开始线程:" + self.name)
        Get_stock_detail(self.fund_url, self.fund_name, self.begin_num, self.end_num)
        print ("退出线程:" + self.name)

      
[Python] 纯文本查看 复制代码
#多线程获取基金持仓明细入口,begin_num是基金列表的开始位置,end_num是基金列表的结束位置
def Get_stock_detail(fund_url, fund_name, begin_num, end_num):
    #初始化mysql连接
    mydb = mysql.connector.connect(
        host="10.23.1.132",
        user="root",
        passwd="abc123",
        database="ofstock"
    )

    cursor = mydb.cursor()

    while begin_num < end_num:
        global nPos
        nPos += 1   #用于进度条显示
        stock_name, amount = hold_a_position(fund_url[begin_num])  # 遍历持仓信息,返回持仓股票名称、持仓量---数组

        for j in range(len(stock_name)):
            sql = "INSERT INTO ofstockinfo (fund_url, fund_name, stock_name, amount) VALUES (%s, %s, %s, %s)" % (
            repr(fund_url[begin_num]), repr(fund_name[begin_num]), repr(stock_name[j]), repr(amount[j]))
            try:
                cursor.execute(sql)
                mydb.commit()
                #print("插入记录成功")
            except Exception as e:
                mydb.rollback()
                print(e.args, "插入记录失败")
        begin_num += 1

    mydb.close()

      我设置的是10个线程,速度一般吧,但是我的机器的cpu差不多100%,还好不是一直100%,凑合半个多小时差不多跑完。
      另外原代码用的是mongodb,我在win2008机器装不上!!!进度卡在70%左右,放弃了,改用mysql,后面再研究吧。
      实现3:
      
[Python] 纯文本查看 复制代码
#进度条实现def progress_bar(portion, total):
    """
    total 总数据大小,portion 已经传送的数据大小
    :param portion: 已经接收的数据量
    :param total: 总数据量
    :return: 接收数据完成,返回True
    """
    part = total / 50  # 1%数据的大小
    count = math.ceil(portion / part)
    sys.stdout.write('\r')
    sys.stdout.write(('[%-50s]%.2f%%' % (('>' * count), portion / total * 100)))
    sys.stdout.flush()

    if portion >= total:
        sys.stdout.write('\n')
        return True

     这是网上找的,是哪里的,嘿嘿,忘了!!原谅我厚颜无耻吧。

     整体实现完成,用到的python的是多进程、xpath、mysql等吧,初次接触,如果有什么错误,还望不吝赐教。跑下来数据还是存在有问题的数据,需要对结果处理下,语句:delete  from ofstockinfo where amount not REGEXP '(^[0-9]+.[0-9]+$)|(^[0-9]$)'

     忘记了,提上表结构:"CREATE TABLE ofstockinfo (fund_url VARCHAR(255), fund_name VARCHAR(100),stock_name VARCHAR(100),amount VARCHAR(100))"


   下载: Get_Ofund_StockDetails.rar (3.58 KB, 下载次数: 633)


  

免费评分

参与人数 5吾爱币 +4 热心值 +5 收起 理由
sssssacc + 1 + 1
stone009 + 1 + 1 我很赞同!
表弟用毛线 + 1 + 1 我很赞同!
蓝筹 + 1 + 1 热心回复!
安卓iphone + 1 用心讨论,共获提升!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| zxl4689 发表于 2020-2-1 14:19
山心豆 发表于 2020-1-26 15:06
请问Python里面的MySQL如何设置?pip install mysql吗?

https://www.runoob.com/python3/python-mysql-connector.html
yangyang2 发表于 2020-3-29 20:50
有两个问题想请问下,
hold_a_positon函数里面
element_result = is_element(driver, "tol")  # 是否存在这个元素,用于判断是否有持仓信息;
    #print(url + '\n')
    if element_result == True:  # 如果有持仓信息则爬取;
        wait = WebDriverWait(driver, 3)  # 设置一个等待时间
        input = wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'tol')))  # 等待这个class的出现;
1、这里的‘tol’元素我查看了网页,就算有持仓信息也没找到这个元素啊,不知道是不是网页改了还是什么原因
2、if element_result == True:  这里不是已经等待到元素了,为什么在之后的条件语句中又等待一次?
Bruce_HD 发表于 2020-1-13 10:31
andykeos 发表于 2020-1-13 10:44
多谢分享
光年之外 发表于 2020-1-13 10:46
感谢分享
Schalkiii 发表于 2020-1-13 10:50
tushare有现成的api直接调用。没必要用chrome driver抓
superman88 发表于 2020-1-13 10:51
我最近也在学习python,纯编程小白,看的小甲鱼的,一起努力
蓝筹 发表于 2020-1-13 11:08
还有密码?爱签到的呀
huangzy97 发表于 2020-1-13 11:10
楼主秀啊
 楼主| zxl4689 发表于 2020-1-13 11:12
蓝筹 发表于 2020-1-13 11:08
还有密码?爱签到的呀

统一的52pojie
 楼主| zxl4689 发表于 2020-1-13 11:20
Schalkiii 发表于 2020-1-13 10:50
tushare有现成的api直接调用。没必要用chrome driver抓

优秀啊,谢谢。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-25 11:40

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表