科研神器(1)——python實現自動讀取英文文獻翻譯并生成綜述

經常看到公眾號推什么“學姐一年發五篇SCI,原來是靠它”之類的文章,點進去全是Python的安利,然而跟著廣告進去也不一定能學會。正好時值學校要求寫文獻閱讀報告,然而至少一萬字的報告實在是讓人望而卻步,于是想到了自己使用Python去實現自動讀取英文文獻,并翻譯生成綜述(當然,只適用于湊字數的情況,如果需要寫綜述,還是建議自己總結歸納)。這里分享給大家,希望大家都能學廢(不是)

如果不想了解技術細節,只想直接拿來用,可以直接跳過代碼編寫部分,直達最后代碼使用部分。

本代碼免費開源,已發布release,可在GitHub上進行下載
,如果你覺得好用,希望能夠給我一個贊(能GitHub給一顆Star更好),也歡迎去github發表意見建議。

代碼實現效果如下:


實現效果

開發環境

  • Windows 10
  • Sublime Text 3
  • Python 3.7
  • Pdfminer、requests
  • 有道翻譯API

事前準備

接口申請

本代碼使用了有道翻譯的API,因此,如需使用,需要去有道翻譯接口官方申請APP Key和Secret key,直接按照其官方教程申請即可,后續需要在代碼中配置。接口申請完全免費,初始會送100元的面值,用完需要續費,不過一般情況100元可以用很久了。

安裝pdfminer和requests

同時按下鍵盤win+R,在彈出的輸入框內輸入cmd按下回車,在彈出的黑框中進行下面操作。
因為我使用的是python3,因此輸入以下命令安裝:

pip install pdfminer3k
pip install requests

需求分析

這里分析了本代碼實現的關鍵點:

  • 文獻是已經下載下來的pdf文件
  • 文獻中,需要提取的部分主要為:
    • 標題
    • 作者
    • 摘要
    • 結論

因此,本代碼的思路是讀取本地文件夾內的pdf文件,然后讀取并識別出其關鍵元素,調用有道翻譯的API進行翻譯,并進行有機組合,寫入TXT文件中。

代碼編寫

讀取pdf文件

依次讀取文件夾內的文件,如果后綴為pdf,則寫入文件元祖:

def getFileName(filepath):
    file_list = []
    for root,dirs,files in os.walk(filepath):
        for filespath in files:
            if 'pdf' in filespath.split('.')[1]:
                file_list.append(os.path.join(root,filespath))
    return file_list

讀取文件內容并提取標題、作者、摘要和結論

def parse(DataIO, save_path, appKey, appSecret):
 
    #用文件對象創建一個PDF文檔分析器
    parser = PDFParser(DataIO)
    #創建一個PDF文檔
    doc = PDFDocument()
    #分析器和文檔相互連接
    parser.set_document(doc)
    doc.set_parser(parser)
    #提供初始化密碼,沒有默認為空
    doc.initialize()
    #檢查文檔是否可以轉成TXT,如果不可以就忽略
    if not doc.is_extractable:
        raise PDFTextExtractionNotAllowed
    else:
        #創建PDF資源管理器,來管理共享資源
        rsrcmagr = PDFResourceManager()
        #創建一個PDF設備對象
        laparams = LAParams()
        #將資源管理器和設備對象聚合
        device = PDFPageAggregator(rsrcmagr, laparams=laparams)
        #創建一個PDF解釋器對象
        interpreter = PDFPageInterpreter(rsrcmagr, device)
        last_para = '' # 記錄上一段文本
        count = 0 # 對文本塊進行計數,方便后續查找標題和作者
        author = '' # 記錄作者
        ab_count = 0 # 記錄已識別的摘要的數量,避免提取文中的abstract

        fanyi = YouDaoFanyi(appKey, appSecret)
        #循環遍歷列表,每次處理一個page內容
        #doc.get_pages()獲取page列表
        for page in doc.get_pages():
            interpreter.process_page(page)
            #接收該頁面的LTPage對象
            layout = device.get_result()
            #這里的layout是一個LTPage對象 里面存放著page解析出來的各種對象
            #一般包括LTTextBox,LTFigure,LTImage,LTTextBoxHorizontal等等一些對像
            #想要獲取文本就得獲取對象的text屬性
            for x in layout:
                try:
                    if(isinstance(x, LTTextBoxHorizontal)):
                        with open('%s' % (save_path), 'a', encoding='utf-8') as f:
                            result = x.get_text() # 每塊的內容
                            # print(result)
                            # 提取標題
                            if count==0:
                                # 如果是researchgate的文章,直接翻頁
                                if re.findall('^see discussions', result.lower())!=[]:
                                    break
                                # 如果第一行是各種頁眉等干擾信息,直接略過
                                if re.findall('(^[0-9])|(^(research )?article)|(unclassified)|(www.)|(accepted (from|manuscript))|(proceedings of)|(vol.)|(volume \d)|(https?://)|(^ieee)|(sciencedirect)|(\d{4}\)$)|(\d{1,4} – \d{1,4}$)|(cid:)',re.split('\s+$',result.lower())[0])!=[] or '':
                                    count -= 1
                                else:
                                    # 將結果寫入TXT
                                    f.write('\n'+result.replace('\n', '')+'\n')
                            # 提取作者
                            elif count==1:
                                # 只取第一作者
                                author = result.split('\n')[0].split(',')[0].split(' and ')[0]
                                author = generate_author(author)
                                print('author '+ author)
                            # 去掉pdf文件讀取的各種換行符
                            result = result.replace('\n', '')
                            try:
                                # 轉為小寫,去掉空格,方便正則識別
                                last_para = last_para.lower().replace(' ', '')
                                # print(result)
                                # 匹配Abstract和摘要內容分開的情況
                                if re.findall('abstract$', last_para)!=[]:
                                    # 去掉關鍵詞
                                    oringin_result = re.split('(K|k)(eyword|EYWORD)[sS]?',result)[0]
                                    # 翻譯并轉換人稱
                                    trans_result = fanyi.translate(oringin_result).replace('我們', '他們')
                                    # print(result)
                                    # 組織語言寫入TXT
                                    write_cont = author + '等人提出:' + trans_result + '\n'
                                    ab_count += 1
                                    f.write(write_cont)
                                # 匹配Abstract和摘要內容位于同一行的情況
                                elif re.findall('^abstract', result.lower().replace(' ', ''))!=[] and re.findall('abstract$', result.lower().replace(' ', ''))==[]:
                                    # 確保摘要只匹配一次,不匹配文中的Abstract字眼
                                    if ab_count==0:
                                        # 去掉Abstract字眼及其后續的符號
                                        oringin_result = re.sub('(a|A)(bstract|BSTRACT)[- —.]?','', result)
                                        # 去掉關鍵詞
                                        oringin_result = re.split('(K|k)(eyword|EYWORD)[sS]?',oringin_result)[0]
                                        # 翻譯并轉換人稱
                                        trans_result = fanyi.translate(oringin_result).replace('我們', '他們')
                                        # print(result)
                                        # 組織語言寫入TXT
                                        write_cont = author + '等人提出:' + trans_result + '\n'
                                        ab_count += 1
                                        f.write(write_cont)
                                # 匹配結論
                                elif re.findall('(^(i|v|x|\d)*\.?conclusions?)|(conclusions?$)', last_para)!=[]:
                                        # 避免因圖表在標題下方導致的識別錯誤
                                        if re.findall('^fig', result.lower()):
                                            continue
                                        # 翻譯
                                        trans_result = fanyi.translate(result)
                                        # print(result)
                                        # 轉換人稱
                                        write_cont = trans_result.replace('我們', '他們') + '\n'
                                        # 寫入TXT
                                        f.write(write_cont)
                            except Exception as e:
                                print(e)
                            last_para = result
                            count += 1
                except Exception as e:
                    print('out'+str(e))
            else:
                continue
        with open('%s' % (save_path), 'a', encoding='utf-8') as f:
            f.write('\n')

按照引用的格式生成作者信息

def generate_author(author):
    # 過濾掉作者名后面的各種符號,并生成引用的格式
    # print(author)
    author = re.sub('by |[\s\d\*?\/@?\(\&\)]+$', '', author)
    author_list = re.split('\s+',author)
    author_str = author_list[len(author_list)-1]
    for i in range(0,len(author_list)-1):
        author_str = author_str + ' ' + author_list[i][0]
    return author_str

翻譯接口

其實直接抄有道官網文檔就可以了,這里在其基礎上做了更改:

class YouDaoFanyi:
    def __init__(self, appKey, appSecret):
        self.YOUDAO_URL = 'https://openapi.youdao.com/api/'
        self.APP_KEY = appKey  # 應用id
        self.APP_SECRET = appSecret  # 應用密鑰
        self.langFrom = 'en'   # 翻譯前文字語言,auto為自動檢查
        self.langTo = 'zh-CHS'     # 翻譯后文字語言,auto為自動檢查
        self.vocabId = "您的用戶詞表ID" #非必填項,可以不寫

    def encrypt(self,signStr):
        hash_algorithm = hashlib.sha256()
        hash_algorithm.update(signStr.encode('utf-8'))
        return hash_algorithm.hexdigest()


    def truncate(self,q):
        if q is None:
            return None
        size = len(q)
        return q if size <= 20 else q[0:10] + str(size) + q[size - 10:size]

    def do_request(self,data):
        headers = {'Content-Type': 'application/x-www-form-urlencoded'}
        return requests.post(self.YOUDAO_URL, data=data, headers=headers)


    def translate(self,q):
        data = {}
        data['from'] = self.langFrom
        data['to'] = self.langTo
        data['signType'] = 'v3'
        curtime = str(int(time.time()))
        data['curtime'] = curtime
        salt = str(uuid.uuid1())
        signStr = self.APP_KEY + self.truncate(q) + salt + curtime + self.APP_SECRET
        sign = self.encrypt(signStr)
        data['appKey'] = self.APP_KEY
        data['q'] = q
        data['salt'] = salt
        data['sign'] = sign
        data['vocabId'] = self.vocabId

        response = self.do_request(data)
        contentType = response.headers['Content-Type']
        result = json.loads(response.content.decode('utf-8'))['translation'][0]
        print(result)
        return result

最后,書寫主函數進行調用:

if __name__ == '__main__':
    #解析本地PDF文本,保存到本地TXT
    folder = '文件夾路徑' # 需要讀取pdf的文件夾的路徑,注意為絕對路徑,如:E:/論文
    write_txt_file = 'result.txt' # 保存結果的文件
    appKey = '應用ID'  # 應用id
    appSecret = '應用秘鑰'  # 應用密鑰
    success_count = 0 # 統計成功的次數
    fail_count = 0 #統計失敗的次數

    # 單次調用,供開發測試
    # pdf_filename = folder+'文件名'
    # with open(pdf_filename,'rb') as pdf_html:
    #     try:
    #         parse(pdf_html, folder + write_txt_file, appKey, appSecret)
    #         success_count+=1
    #     except Exception as e:
    #         print(pdf_filename)
    #         fail_count+=1

    pdf_list = getFileName(folder)
    # 依次讀取元祖,獲取pdf文件位置
    for file_item in pdf_list:
        with open(file_item,'rb') as pdf_html:
            try:
                print(file_item)
                parse(pdf_html, folder + write_txt_file, appKey, appSecret)
                success_count+=1
            except Exception as e:
                # 文件讀取或翻譯失敗則將錯誤信息寫入TXT
                print('文檔讀取失敗:' + str(e) +',路徑為:' + file_item)
                with open('%s' % (folder + write_txt_file), 'a', encoding='utf-8') as f:
                    f.write('\n'+'文檔讀取失敗:' + str(e) +',路徑為:' + file_item + '\n')
                fail_count+=1

    print('共讀取pdf文件' + str(success_count+fail_count) + '個,其中成功讀取并翻譯' + str(success_count) + '個,失敗' + str(fail_count) + '個')

至此,代碼編寫完畢

使用

本工具已發布release,可在GitHub上進行下載,直接雙擊使用即可,后面的可以不用看了。

也可下載源碼使用,代碼可在Github上下載

配置代碼

更改代碼主函數的配置變量(其中的應用ID和應用秘鑰需要事先申請,見上文事前準備一節):

if __name__ == '__main__':
    #解析本地PDF文本,保存到本地TXT
    folder = '文件夾路徑' # 需要讀取pdf的文件夾的路徑,注意為絕對路徑,如:E:/論文/
    write_txt_file = 'result.txt' # 保存結果的文件
    appKey = '應用ID'  # 應用id
    appSecret = '應用秘鑰'  # 應用密鑰

運行代碼

運行代碼之前,需要安裝python3以及按照事前準備中的描述安裝pdfminer和requests

在代碼所在的根目錄下的命令行中輸入以下命令即可:

python pdfprocessor.py

運行結果

僅花了38秒的時間,就提取并翻譯完成了14個pdf文件,翻譯生成的字數合計6812個字:

運行結果

試了一下45個文件,花了大概兩分鐘,生成了一萬多字

最后看一下翻譯結果對比:

翻譯結果中英對比
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,491評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,263評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,946評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,708評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,409評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,939評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,774評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,641評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,872評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,650評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容