PYTHON实战:从百度资讯爬取信息作为正文,巨潮资讯网爬取有关PDF作为附件,自动发送邮件(Mysql做存储)

小小的前言:

鄙人在学习过程中写的小实践.效率不高.大佬勿喷.

  1. 其中的强制等待严重影响运行速度.建议使用隐式或者显示等待
  2. 用Pyqt5画了个简单的GUI(xwin.ui)
    然后通过pyuic -o xwin.py xwin.ui 就可以转为py文件了
    然后通过按钮触发druging函数.因为我不会使用按钮传参数.
    所以def了一个中介函数哈哈哈哈哈哈.勿喷.看到代码就懂了
  3. 其中try except异常处理是我对很多情况进行尝试后得出的设计.
    因为百度和巨潮对我这个实战不是特别友好.哈哈哈哈哈哈哈.
    下次设计实战的时候看来还是要有针对性一点
  4. 因为对自己电脑性能不抱有太大希望.所以把百度上爬取的咨询都存进了Mysql,再进行筛选.(Mysql8.0 . 数据库名为pachong . 表名为test . 建表语句在最后)

先把PYQT5安装上

安装方法我也是在csdn上白嫖的
参考大佬链接:
上面能学到不少,当然,觉得这个麻烦可以跳过.直接在源代码中调用下面的druging函数即可

SMTP码

因为涉及到发送邮件.我们需要获得自己的SMTP码
以qq邮箱为例.如下.图中的qq为小弟本人.希望可以和各位交流!
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
把获得的SMTP码复制到mainmian.py文件里,源代码有标注哈

分步的想法就不细说啦.注释里写的比较清楚

xwin.py是用pyuic5自动生成的.自然是没有注释的哈

源代码

xwin.py(窗口文件)

默认的爬取的是百度集团!都可以自己修改哈
在这里插入图片描述

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'xwin.ui'
#
# Created by: PyQt5 UI code generator 5.12.3
#
# WARNING! All changes made in this file will be lost!


from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(783, 419)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(520, 110, 191, 131))
        self.pushButton.setStyleSheet("font: 36pt \"Adobe Devanagari\";")
        self.pushButton.setObjectName("pushButton")
        self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
        self.lineEdit.setGeometry(QtCore.QRect(230, 110, 251, 51))
        self.lineEdit.setStyleSheet("font: 18pt \"Adobe Devanagari\";")
        self.lineEdit.setObjectName("lineEdit")
        self.lineEdit_2 = QtWidgets.QLineEdit(self.centralwidget)
        self.lineEdit_2.setGeometry(QtCore.QRect(230, 190, 251, 51))
        self.lineEdit_2.setStyleSheet("font: 16pt \"Adobe Devanagari\";")
        self.lineEdit_2.setObjectName("lineEdit_2")
        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setGeometry(QtCore.QRect(50, 110, 151, 51))
        self.label.setStyleSheet("font: 20pt \"Adobe Devanagari\";")
        self.label.setObjectName("label")
        self.label_2 = QtWidgets.QLabel(self.centralwidget)
        self.label_2.setGeometry(QtCore.QRect(50, 190, 151, 51))
        self.label_2.setStyleSheet("font: 20pt \"Adobe Devanagari\";")
        self.label_2.setObjectName("label_2")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 783, 26))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.pushButton.setText(_translate("MainWindow", "Let\'s Go"))
        self.lineEdit.setText(_translate("MainWindow", "百度集团"))
        self.lineEdit_2.setText(_translate("MainWindow", "[email protected]"))
        self.label.setText(_translate("MainWindow", "公司名称"))
        self.label_2.setText(_translate("MainWindow", "邮箱地址"))

manman.py(主文件)

import re
from selenium import webdriver
import pymysql
import time
import warnings
import requests
import datetime
import pdfplumber
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
warnings.filterwarnings("ignore")
def druging(company,mailaddr):
    today = time.strftime('%Y-%m-%d')
    db = pymysql.connect(host='localhost', port=3306, user='root', password='123456', database='pachong',
                         charset='utf8')#使用pymysql连接数据库,具体参数就不说了,一眼就能看懂!
    cur = db.cursor()#游标方法,用来操作数据库,相当于固定用法
    def getday(n): #设计一个getday()函数,输入n就返回第前n天的日期    例如:今天是 2021-04-19 getday(1)返回2021-04-18
        today = datetime.date.today()
        date_delta = datetime.timedelta(n)
        ask_day = today - date_delta
        ask_day = ask_day.strftime('%Y-%m-%d')
        return ask_day
    def std_time(time_list): #这个函数用来把百度资讯对应的时间都转换成统一格式,这里选择了'%Y-%m-%d'
        #百度资讯据我所知有6种日期格式
        #1.昨天 2.前天 3.X天前 4.X分钟前/X小时前 5.X年X月X日 6.X月X日
        for i in range(len(source_time)):
            if '昨天' in source_time[i]:#解决第一种日期
                source_time[i] = getday(1)
            elif '前天' in source_time[i]:#解决第二种日期
                source_time[i] = getday(2)
            elif '天前' in source_time[i]:#解决第三种日期
                a = str(source_time[i][0])
                source_time[i] = getday(int(a))
            elif '分钟' in source_time[i] or '小时' in source_time[i]:#解决第四种日期
                source_time[i] = getday(0)
            elif '年' in source_time[i]:#解决第五种日期
                source_time[i] = time.strftime('%Y-%m-%d')
            else:#解决第六种日期
                try:#用的是字符串拼接的方式.大佬肯定有更好的办法!
                    if len(source_time[i].split('月')[0]) == 1:
                        source_time[i] = '0' + str(source_time[i])
                    if len(source_time[i].split('月')[1]) == 2:
                        source_time[i] = str(source_time[i])[:3] + '0' + str(source_time[i])[-2:]
                    source_time[i] = str(today)[:4] +'-'+str(source_time[i])
                    source_time[i] = re.sub('年', '-', source_time[i])
                    source_time[i] = re.sub('月', '-', source_time[i])
                    source_time[i] = re.sub('日', '', source_time[i])
                except:#使用异常处理的原因是有些百度的资讯没有发布时间.导致报错,所以没有日期的赋值1000年01月01日
                    source_time[i] = '1000年01月01日'
                    source_time[i] = re.sub('年', '-', source_time[i])
                    source_time[i] = re.sub('月', '-', source_time[i])
                    source_time[i] = re.sub('日', '', source_time[i])
        return time_list
    keys = ['违约', '诉讼', '兑付', '风险', '不稳定'] #随便编了几个关键字,在正文中出现这几个单词就扣5分
    headers = {
    
    'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) \
             AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 \
             Mobile Safari/537.36 Edg/89.0.774.68'}
    for n in range(3):#爬取三页的资讯
        page = 10 * n
        url = 'https://www.baidu.com/s?tn=news&rtt=4&bsst=1&cl=2&wd=' + company + '&medium=0&pn=' + str(page)
        text_all = requests.get(url, headers=headers).text
        #下面是使用正则表达式对爬取到的资讯进行处理,非贪婪匹配的道理就不多说了!
        p_href = '<h3 class=".*?<a href="(.*?)"'
        p_title = '<h3 class=".*?<a href=.*?target.*?>(.*?)</a>'
        p_source = '<div class="news.*?<span class="c-color.*?">(.*?)</div>'
        href = re.findall(p_href, text_all, re.S)  # href不需要额外的处理了
        title = re.findall(p_title, text_all, re.S)  # title是标题名
        source = re.findall(p_source, text_all, re.S)
        href_text = []
        for i in range(len(title)):
            title[i] = re.sub('<.*?>', '', title[i])  # 去掉标签名
            title[i] = title[i].strip()  # 去掉空格
            href_text.append(requests.get(url=href[i], headers=headers).text)
        source_time = []#资讯发布时间
        source_place = []#资讯来源
        for i in range(len(source)):#因为爬取到的source[]同时含有来源和时间,所以做切割,
            # 前半部分是来源,存入source_place,后半部分存入source_time
            source[i] = re.sub('<.*?>', '', source[i])
            source[i] = source[i].strip()
            source_place.append(source[i].split()[0])
            try:
                source_time.append(source[i].split()[1])
            except:
                source_time.append('1000年01月01日')
        std_time(source_time)
        sql1 = 'select * from test where company=%s'
        sql2 = 'insert test(company,title,href,place,date,score) values (%s,%s,%s,%s,%s,%s)'
        #检查数据库中的内容 如果标题和数据库中已有的重合,则不再存入数据库
        cur.execute(sql1, company)
        check = cur.fetchall() #check为所有数据库返回的数据
        check_list = []
        score = []#评分列
        for k in range(len(check)):
            check_list.append(check[k][1])
        for k in range(len(title)):
            num = 0
            for m in keys:
                if m in title[k] or m in href_text[k]:
                    num -= 5
            score.append(num)
        for k in range(len(title)):
            if title[k] not in check_list:
                cur.execute(sql2, (company, title[k], href[k], source_place[k], source_time[k], score[k]))
        db.commit()
    sql = ('select * from test where company=%s and score<0 and (date=%s or date=%s or date=%s or date=%s or date=%s)')
    data_list = []
    data_list.append('<p>尊敬的用户,您好,以下是今天的舆情监控报告,望查阅:</p>') #邮件正文第一行,需要以html格式书写,
                                # 因为后面的资讯要用到的超链接,统一使用html格式很方便
    user = '[email protected]'
    pwd = SMTP
    #######################填入自己的 引号别忘记
    to = mailaddr
    cur.execute(sql, (company, today,str(getday(1)),str(getday(2)),str(getday(4)),str(getday(3))))
    data = cur.fetchall()
    db.commit()
    cur.close()
    db.close()
    for j in range(len(data)):#使用字符串拼接,完成html语句
        data_list.append('<p><a href="' + str(data[j][2]) + '">'
                         + str(j + 1) + '.' + str(data[j][1]) + '</a></p>')
    data_list = '\n'.join(data_list)
    #有关PDF
    #使用selenium库模仿真人使用谷歌浏览器下载PDF文件
    chrome_options = webdriver.ChromeOptions()
    chrome_options.add_argument('--headless')#设置无窗口模式
    browser = webdriver.Chrome(options=chrome_options)
    url = 'http://www.cninfo.com.cn/new/fulltextSearch?notautosubmit=&keyWord='+str(company)
    #又是爬取巨潮资讯网.和百度爬取差不多
    browser.get(url)
    time.sleep(3)
    data = []
    data.append(browser.page_source)
    p_title = '<span.*?class="r-title">(.*?)</span>'
    p_href = '<a target="_blank" href="(.*?)" data-id='
    p_time = '<span class="time">(.*?)</span>'
    #下面是爬取PDF的页数
    # for i in range(2):
    #     browser.find_element_by_xpath('//*[@id="fulltext-search"]/div/div[1]/div[2]/div[4]/div[2]/div/button[2]/i').click()
    #     time.sleep(2)
    #     data_tmp = browser.page_source
    #     data.append(data_tmp)
    #     time.sleep(2)
    data = ''.join(data)
    title = re.findall(p_title, data, re.S)
    href = re.findall(p_href, data)
    time_time = re.findall(p_time, data, re.S)
    for i in range(len(title)):
        title[i] = title[i].strip()
        title[i] = re.sub('<.*?>','', title[i])
        href[i] = 'http://www.cninfo.com.cn' + str(href[i])
        href[i] = re.sub('amp;', '', href[i])
        time_time[i] = time_time[i].strip()
    #只要今年的数据
    for i in range(len(time_time)):
        if '2021' in time_time[i]:
            time_time[i] = time_time[i]
        else:
            time_time[i] = ''
            title[i]=''
            href[i]=''
    #删除所有值为空的列表值     不直接删除,而先赋值空值再删除的原因是,直接删除会导致下标改变,在循环中会出问题
    while '' in title:
        title.remove('')
    while '' in href:
        href.remove('')
    while '' in time_time:
        time_time.remove('')
    prefs = {
    
    'profile.default_content_settings.popups': 0, 'download.default_directory': 'C:\\Users\\70493\\Desktop\\数据挖掘实战'}
    chrome_options.add_experimental_option('prefs', prefs)
    browser = webdriver.Chrome(options=chrome_options)
    for i in range(len(href)):
        browser.get(href[i])
        try: #selenium中的元素定位方式,这个不提了 复制Xpath即可
            browser.find_element_by_xpath('//*[@id="noticeDetail"]/div/div[1]/div[3]/div[1]/button').click()#单击下载按钮
            time.sleep(3)
        except:#因为有些资讯不包含PDF文件,所以把这些赋值为空,等等删除
            href[i]=''
            title[i]=''
            time_time[i]=''
        if ':' in title[i]:
            title[i]=''
            href[i]=''
            time_time[i]=''
    while '' in title:
        title.remove('')
    while '' in href:
        href.remove('')
    while '' in time_time:
        time_time.remove('')
    pdfs = []
    #PDF结束,以下是邮件发送
    for i in range(len(title)):
        title[i]=title[i].replace(' ','')
        try:#这里的try except是因为下载的pdf文件名有时会有出入.具体运行就知道了!
            pdf = pdfplumber.open(str(str(company)+':'+str(title[i])+'.PDF'))
          
            pages = pdf.pages
            text_all = []
            for m in range(len(pages)):
                text = pages[m].extract_text()
                text_all.append(text)
            text_all = ''.join(text_all)
            if ('持有' in text_all) or ('议案' in text_all) or ('理财' in text_all) or ('股東' in text_all):
                pdfs.append(str(str(company) + ':' + str(title[i]) + '.PDF'))
            pdf.close()
        except:
            pdf = pdfplumber.open(str(title[i]+'.PDF'))
            pages = pdf.pages
            text_all = []
            for m in range(len(pages)):
                text = pages[m].extract_text()
                text_all.append(text)
            text_all = ''.join(text_all)
            if ('持有' in text_all) or ('议案' in text_all) or ('理財' in text_all) or ('股東' in text_all):
                #正文出现如上关键字,筛选出来,等等用来当作附件,若不需要筛选,可以删除,如果爬取的是港股.PDF多半为繁体,别忘记把关键字修改以下
                pdfs.append(str((title[i]+'.PDF')))
            pdf.close()
    msg = MIMEMultipart()
    msg.attach(MIMEText(data_list, 'html', 'utf8'))
    for i in range(len(pdfs)):#将所有符合要求的pdf添加近附件
        att=MIMEText(open(pdfs[i],'rb').read(),'base64','utf-8')
        att["Content-Type"] = 'application/octet-stream'
    # 下面的filename是在邮件中显示的文件名及后缀名,文件名可不同,但不能有中文
        att.add_header('Content-Disposition', 'attachment', filename=str('wudimiji'+str(i+1)+'.PDF'))
        #命名为wudimiji 就是无敌秘籍的意思
        msg.attach(att)#添加附件
    msg['Subject'] = '最新情报资讯'#设置主题
    msg['From'] = user#发送人
    msg['To'] = to#收件人
    s = smtplib.SMTP_SSL('smtp.qq.com', 465)#服务器选择
    s.login(user, pwd)
    s.send_message(msg)#发送的内容
    s.quit()



建表语句

CREATE TABLE `test` (
  `company` varchar(30) NOT NULL DEFAULT '',
  `title` varchar(150) NOT NULL DEFAULT '自创',
  `href` varchar(100) NOT NULL DEFAULT 'www.baidu.com',
  `place` varchar(50) NOT NULL DEFAULT 'unknown',
  `date` varchar(20) NOT NULL DEFAULT '0',
  `score` varchar(5) NOT NULL DEFAULT '100'
) ENGINE=InnoDB DEFAULT CHARSET=utf8

在这里插入图片描述
然后差不多就是这个样子啦!
还有.关于有些时候这个公司名字让我很困惑.因为假如输入’百度集团’‘运行程序会报错,因为下载出来的PDF抬头是’百度集团-SW’,所以会找不到file打开.我尝试了使用正则表达式…但是好像没法用在pdfplum.open()里,有没有大神能解决? 如果把具体名字输进程序,基本是不会报错的!
多谢

猜你喜欢

转载自blog.csdn.net/zznanyou/article/details/115862088