美文网首页已收录(2017-8-15)工具癖简摘17
Python编程从0到1(实战篇:抓取外网资源自动翻译并发至个人

Python编程从0到1(实战篇:抓取外网资源自动翻译并发至个人

作者: 安和然 | 来源:发表于2018-04-04 11:15 被阅读82次

    1.需求:

    本程序源于我的一个真实需求,需要每天去关注数十个国外网站,并在上面找到有用信息后,编译为文章。

    就想能不能写一个程序,每天早上帮我去数十个网站上,看有没有最新的文章,爬取下来,自动翻译后发到我的邮箱里。

    2.设计思路:

    设计思路.jpg

    3.各模块代码解析

    3.1 初始化目标网站及抓取规则

    由于后期需要对爬取的网站和规则进行修改完善,因此,把目标网站和规则等内容单独做在一个Config.json文件中,每次程序运行都调用这个配置文件。

    生成Config.json的源码:

    import json
    
    #本文档用于生成siteconfig.json文件。
    
    config = [{
        'sitename':'美国白宫官网',
        'starturl':'https://www.whitehouse.gov/issues/economy-jobs/',
        'listXpath':r'//article//h2/a/@href',
        'OriginalUrl':"",
        'titleXpath':r'//*[@id="main-content"]/div/div/h1/text()',
        'timeXpath':r'//*[@id="main-content"]/div[1]/div/div/p/time/text()',
        'contentXpath':r'//*[@id="main-content"]/div[2]/div/div/p/text()',
        'authorXpath':r'//*[@id="main-content"]/div[1]/div/p/text()'
    },
        {
            'sitename': '美国财政部官网',
            'starturl': 'https://home.treasury.gov/news/press-releases/',
            'listXpath': r'//*[@id="block-hamilton-content"]//h2/a/@href',
            'OriginalUrl':"https://home.treasury.gov",
            'titleXpath': r'//*[@id="block-hamilton-page-title"]/h1/span/text()',
            'timeXpath': r'//*[@id="block-hamilton-content"]/article/div//time/text()',
            'contentXpath': r'//*[@id="block-hamilton-content"]/article/div//p/text()',
            'authorXpath': r''
        },
        {
            'sitename': '美国国会预算办公室',
            'starturl': 'https://www.cbo.gov/most-recent',
            'listXpath': r'//*[@id="content"]/div//span/a/@href',
            'OriginalUrl': "https://www.cbo.gov",
            'titleXpath': r'//*[@id="page-title"]/text()',
            'timeXpath': r'//*[@class="date-display-single"]/text()',
            'contentXpath': r'//*[@id="content-panel"]//p/text()',
            'authorXpath': r''
        }
    ]
    with open("siteconfig.json", "w") as text:
        json.dump(config, text)
    

    其中,config字典的解释如下:

     {
            'sitename':  #网站名
            'starturl':  #开始url地址
            'listXpath':  #文章列表的Xpath表达式
            'OriginalUrl': #原始url,用于拼接地址
            'titleXpath':  #文章标题的Xpath表达式
            'timeXpath': #文章时间的Xpath表达式
            'contentXpath':   #文章内容的Xpath表达式
            'authorXpath': #文章作者的Xpath表达式
        },
    

    3.2 抓取网页内容

    生成一个seen.json文件,保存已经抓取过的页面网址,不用重复抓取。
    传递的参数为config.json转成的字典列表。
    IOarticle函数负责生成md文档。

    import json
    from lxml import etree
    import datetime
    import SendEmail
    
    def getHTMLText(url):
        try:
            r = requests.get(url, timeout = 30)
            r.raise_for_status()
            #r.encoding = 'utf-8'
            return r.text
        except:
            return ""
    
    def getLinkList(dic):
        url = dic['starturl']
        html = getHTMLText(url)
        selector = etree.HTML(html)
        links = selector.xpath(dic['listXpath'])
        return links
    
    def getContent(dic,articles):
        links = getLinkList(dic)
        with open("seen.json") as seen:
            seenLink = json.load(seen)
        print(seenLink)
        for link in links:
            link = dic['OriginalUrl'] + link
            if link in seenLink:
                print("已经抓取过本页面")
                continue
            else:
                print(link)
                seenLink.append(link)
                html = getHTMLText(link)
                selector = etree.HTML(html)
                title = selector.xpath(dic['titleXpath'])
                print(title)
                time = selector.xpath(dic['timeXpath'])
                print(time)
                author = dic['sitename']
                print(author)
                paras = selector.xpath(dic['contentXpath'])
                print(paras)
        #将爬取到的文章用字典格式来存
            article = {
             'Title' : str(title).replace("'"," ").replace('"'," "),
             'Link' : str(link),
             'Time' : str(time).replace("'"," ").replace('"'," "),
             'Paragraph' : str(paras).replace("'"," ").replace('"'," ").replace("//n"," "),
             'Author' : str(author).replace("'"," ").replace('"'," ")
           }
            articles.append(article)
    
        with open("seen.json",'w') as seen:
            json.dump(seenLink,seen)
        return articles
    
    def IOarticle(articles):
        nowTime = datetime.datetime.now().strftime('%Y%m%d')
        filename = nowTime + ".md"
        fo = open(filename, "w+", encoding="utf-8")
        fo.writelines("[TOC]"+ "\n")
        for article in articles:
            fo.writelines("# "+ article['Title'] + "\n")
            fo.writelines("**参考译文:**" + GoogleTransla.translateGoogle(article['Title'].strip('[]')) + "\n")
            fo.writelines("**来源:**" + article['Link'] + "\n")
            fo.writelines(article['Time'].strip() + "\n")
            fo.writelines(GoogleTransla.translateGoogle(article['Time'].strip().replace("'"," ").replace('"'," "))+ "\n")
            fo.writelines("**正文:**" + article['Paragraph'] + "\n")
            try:
                fo.writelines("**参考译文:**" + GoogleTransla.translateGoogle(article['Paragraph'].strip('[').strip(']').replace("'"," ").replace('"'," ") + "\n"))
            except:
                fo.writelines("文本太长,暂时只提供前2000字符的翻译\n")
                fo.writelines("**参考译文:**" + GoogleTransla.translateGoogle(
                    article['Paragraph'][:10000].strip('[').strip(']').replace("'", " ").replace('"', " ") + "\n"))
            fo.writelines("\n **来源网站:**" + article['Author'] + "\n")
            fo.writelines("\n\n")
        fo.close()
        with open("text.json","w") as text:
            json.dump(articles,text)
        return filename
    
    def main():
        articles = []
        with open("siteconfig.json") as site:
            webs = json.load(site)
        for web in webs:
            articles = getContent(web,articles);
        filename = IOarticle(articles)
        SendEmail.send_mail(filename,filename)
        # getWHList(keyWord="china")
    

    3.3 调用Google翻译

    import re
    import urllib.parse, urllib.request
    import urllib
    
    url_google = 'http://translate.google.cn'
    reg_text = re.compile(r'(?<=TRANSLATED_TEXT=).*?;')
    user_agent = r'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) ' \
                      r'Chrome/44.0.2403.157 Safari/537.36'
    
    def translateGoogle(text, f='', t='zh-cn'):
        text = text.strip('[]').replace("'"," ").replace('"'," ")
        values = {'hl': 'zh-cn', 'ie': 'utf-8', 'text': text, 'langpair': '%s|%s' % (f, t)}
        value = urllib.parse.urlencode(values)
        req = urllib.request.Request(url_google + '?' + value)
        req.add_header('User-Agent', user_agent)
        response = urllib.request.urlopen(req)
        content = response.read().decode('utf-8')
        data = reg_text.search(content)
        result = data.group(0).strip(';').strip('\'')
        print(result)
        return result
    
    

    3.4 发送邮件模块

    from email.mime.text import MIMEText
    from email.mime.image import MIMEImage
    from email.mime.base import MIMEBase
    from email.mime.multipart import MIMEMultipart
    from email import encoders
    import smtplib
    import time
    
    
    def send_mail(subject,filename):
        email_host = 'smtp.163.com'  # 服务器地址
        sender = '     '  # 发件人
        password = '   '  # 密码,如果是授权码就填授权码
        receiver = '   '  # 收件人
    
        msg = MIMEMultipart()
        msg['Subject'] = subject  # 标题
        msg['From'] = ''  # 发件人昵称
        msg['To'] = ''  # 收件人昵称
    
        # 正文-图片 只能通过html格式来放图片,所以要注释25,26行
        mail_msg = '''
    <p>\n\t 这是电脑自动发送的邮件!</p>
    <p>\n\t 不必回复。</p>
    <p><a href="https://www.jianshu.com/u/ee5f3fe1b932">简书</a></p>
    <p>如需增加收件人、爬取网站,请联系:QQ:39489421</p>
    <p><img src="cid:image1"></p>
    '''
        msg.attach(MIMEText(mail_msg, 'html', 'utf-8'))
        # 指定图片为当前目录
        # fp = open(r'111.png', 'rb')
        # msgImage = MIMEImage(fp.read())
        # fp.close()
        # # 定义图片 ID,在 HTML 文本中引用
        # msgImage.add_header('Content-ID', '<image1>')
        # msg.attach(msgImage)
    
        ctype = 'application/octet-stream'
        maintype, subtype = ctype.split('/', 1)
        # 附件-图片
        # image = MIMEImage(open(r'111.jpg', 'rb').read(), _subtype=subtype)
        # image.add_header('Content-Disposition', 'attachment', filename='img.jpg')
        # msg.attach(image)
        # 附件-文件
        file = MIMEBase(maintype, subtype)
        file.set_payload(open(filename, 'rb').read())
        file.add_header('Content-Disposition', 'attachment', filename=subject + '.md')
        encoders.encode_base64(file)
        msg.attach(file)
    
        # 发送
        smtp = smtplib.SMTP()
        smtp.connect(email_host, 25)
        smtp.login(sender, password)
        smtp.sendmail(sender, receiver, msg.as_string())
        smtp.quit()
        print('success')
    

    4. 复盘程序编写中遇到的问题:

    4.1 程序间数据交换格式选择

    用Json文件作为程序间交换数据的格式,比较方便,与python中的字典列表很像。

    4.2 爬虫方式的选择

    爬虫有很多现成的框架和方法,如Beautifulsoup,Scrapy,正则表达式等,而Xpath是一种兼具效率和难度的好工具,在Chrome下有一个Xpath helper的插件,可以很方便的调试,还有一个Chrome按下F12,还有一个Copy Xpath的功能,十分方便。可以上网找一些相关文档学习。

    4.3 翻译引擎的使用

    当前比较好的有谷歌翻译、百度翻译、有道翻译,都有比较好的方法,不同的文章,三个翻译引擎结果都不一样,各有优势,这里选了谷歌,但不一定是最好的。
    谷歌翻译传递的参数如果太长,也就是文章如果太长,就会报错,所以我设定了一个限额,只翻译前10000个字符。

    4.3 发送邮箱的坑

    做邮件自动发送这块花了不少时间,主要是现在邮件如果使用SMTP,登录的密码需要使用授权码,而不是密码本身。这点很重要,切记。

    4.3 用Markdown生成最终报告

    Markdown是我比较喜欢的一种方式,因此,使用这个生成后,可以转化为各种格式。

    5 改进方向及展望

    1. google翻译的效果真的很一般,不过有参考价值。
    2. 可以增加关键字,只发送含有关键字的内容。
    3. 目标网站的研究很重要,不同的网站虽然大致一样,但信息源的结构都不一样。一句话:功夫在编程外,要让一个程序发挥作用,需要做更多基础性的工作。

    相关文章

      网友评论

        本文标题:Python编程从0到1(实战篇:抓取外网资源自动翻译并发至个人

        本文链接:https://www.haomeiwen.com/subject/ihqkhftx.html