Python爬虫:爬取华尔街日报的全部历史文章并翻译

文摘   教育   2024-11-24 22:01   中国  

👇 连享会 · 推文导航 | www.lianxh.cn

🍓 课程推荐:连享会:2025 寒假班
嘉宾:连玉君(初级|高级);杨海生(前沿)
时间:2025 年 1 月 13-24 日
咨询:王老师 18903405450(微信)

作者: 许梦洁 (Frankfurt School of Finance and Management)
邮箱: m.xu@fs.de


目录

  • 1. 获取 Cookies

  • 2. 获取文章列表

    • 2.1 网页分析

    • 2.2 代码

    • 2.3 文章列表

    • 2.4 文章年份分布

    • 2.5 文章主题分布

  • 3. 爬取文章内容

    • 3.1 分析网页

    • 3.2 爬取文章代码

    • 3.3 爬取文章样例

  • 4. 翻译

    • 4.1 翻译文章代码

    • 4.2 翻译文章样例

  • 5. 参考文献

  • 6. 相关推文



编者按:
  初识许梦洁同学是 2017 年。我当时刚获得博导资格,兴冲冲地跑去参加直博生见面会,希望能招到我的第一个博士生。流程性的介绍环节之后,是博士生们自由与老师们的自由交流时间。许梦洁和刘畅同学 (我的第一个博士生) 和我交谈了很久。那时,梦洁给我的印象很深刻,作为一个经管类的本科毕业生,她知道很多金融方面的「硬核知识」,同时掌握了 R, Python, Stata 等多种语言/软件的使用。尤其是对编程感兴趣。遗憾的是,她没有成为我的第一个博士生。
  随后的一年多里,我们经常讨论一些编程类的问题,她也应邀做过连享会的助教,颇受学员好评,因为,就大家看来,似乎没有许梦洁搞不定的事情。在中大读了一年多,她就申请到了法兰克福大学,乐呵呵地寻求更宽广的舞台了。期间,零零星星有些交流,估计她应该是超级忙。我只知道她每周都要读很多文献,交很多作业,而写笔记和博客的习惯也一直保持的很好。
  前几日,我突然想起她的 CSDN 博客-磐石若水,居然持续的有博客在分享。虽然有不少东西我已经看不太懂了,但从博客的前两段话,大致了解这些东西都是很有意思,也很重要的选题。因此,我厚着脸皮向梦洁约稿 (要稿),她欣然应许,几天后就发来了三篇按照连享会推文格式编排好的推文。我基本上不需要做任何修改便可分享给诸位。
     连玉君 (2021/9/22 23:41)

温馨提示: 文中链接在微信中无法生效。请点击底部「阅读原文」。或直接长按/扫描如下二维码,直达原文:

从读论文和写论文的体验来看,「传闻证据」 (anecdotes) 对论文能不能给人可靠的第一印象有决定性作用。传闻证据到位了,就不会有人追着问一些「澄清性问题」(clarification questions),后面论证研究题目的重要性时也会顺利很多 (why care)。此外,很多时候传闻证据对作者本人更好地了解研究背景 (institutional background) 并提出合情合理的研究设计也有很大的帮助作用 (写上对应的英语表达是确保读者理解的意思和我表达的意思不出现偏差而做的一个双重解释。)。

据我所知,华尔街日报 (Wall Street Journal) 给很多顶刊论文提供了灵感。比如:

  • Zhu (2019 RFS)
  • 用停车场数据预测零售公司业绩的报道非常相关 (2014.11.20 WSJ)
  • 一篇关于对冲基金经理与大数据的报道 (2013.12.18 WSJ),该文提出了 Katona et al. (2018 WP, MS R&R)  和 Mukherjee et al. (2021 JFE) 的主要论点。

所以我最近写论文的时候爬了华尔街日报的所有历史数据,源网址是 WSJ Archives,事实证明这个工作在我最近的论文展示中起到了相当正面的作用 (ps, 获取 WSJ Archive 信息需要购买 WSJ 账号,本文只做交流学习使用,请勿使用本文内容获利。)

1. 获取 Cookies

按照惯例,还是先获取 Cookies。首先登陆,登陆后等待大概 20 秒左右会跳出一个小框,要求接受 cookies,需要点击 YES, I AGREE, 经过这步操作的 Cookies 才能顺利获取文章列表或文章内容,否则会被网站识别为爬虫。

另外, Cookies 有失效时间 (expiry time),最好每次爬之前都更新下 Cookies。

from selenium import webdriver
import time
import json

option = webdriver.FirefoxOptions()
option.add_argument('-headless')
driver = webdriver.Firefox(executable_path='/Users/mengjiexu/Dropbox/Pythoncodes/Bleier/geckodriver')

# 填入 WSJ 账户信息
email = 'username'
pw = 'password'
  
def login(email, pw):
    driver.get(
        "https://sso.accounts.dowjones.com/login?")
    # 为了不透露个人信息,需要读者自己粘贴登陆界面的 url
    time.sleep(5)
    driver.find_element_by_xpath("//div/input[@name = 'username']").send_keys(email)
    driver.find_element_by_xpath("//div/input[@name = 'password']").send_keys(pw)
    driver.find_element_by_xpath("//div/button[@type = 'submit']").click()

# 登陆
login(email, pw)

time.sleep(20)

# 切换到跳出的小框
driver.switch_to_frame("sp_message_iframe_490357")
# 点击接受收集 Cookies 
driver.find_element_by_xpath("//button[@title='YES, I AGREE']").click()

time.sleep(5)

# 将 Cookies 写入文件
orcookies = driver.get_cookies()
print(orcookies)
cookies = {}
for item in orcookies:
    cookies[item['name']] = item['value']
with open("wsjcookies.txt""w"as f:
    f.write(json.dumps(cookies))

2. 获取文章列表

2.1 网页分析

WSJ 每日文章列表 url 的命名方式十分简单,由以下两部分组成:

  • https://www.wsj.com/news/archive/
  • 年/月/日

所以在指定时间范围内遍历每一天即可得到每一天的文章列表。不过我爬的时候顺手爬了所有的日期列表,所以代码中直接遍历了日期列表。

WSJ Daylist: 链接: https://pan.baidu.com/s/1kPYlot5lmYtQwWlHxA6OpQ  密码: 0b44

以 2021 年 6 月 10 日的文章列表为例 (如下图),对于每一篇文章,主要关注四个变量:

  • 文章所属板块 - 黄色框
  • 文章标题 - 红色框
  • 文章链接 - 绿色框
  • 文章日期 - 前定

很多日期的文章列表都不止一页,所以需要:

  • 判断翻页条件:详见代码中 pagenation(page) 函数
  • 如果满足翻页条件,进行翻页:newdaylink = daylink + "?page=%s"%pagenum

2.2 代码

import time
from lxml import etree
import csv
import re
from tqdm import tqdm
import requests
import json
import pandas as pd
import csv
import unicodedata
from string import punctuation

f = open('wsjdaylist.txt''r')

headers = {'User-Agent''Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:88.0) Gecko/20100101 Firefox/88.0',
"content-type""application/json; charset=UTF-8",
"Connection""keep-alive"
}


def parsearticle(date, daylink):
    with open("wsjcookies.txt""r")as f:
        cookies = f.read()
        cookies = json.loads(cookies)
    session = requests.session()
    url = "https://www.wsj.com" + daylink
    data = session.get(url, headers=headers, cookies = cookies)

    time.sleep(1)
    page = etree.HTML(data.content)

    href = page.xpath("//h2[@class='WSJTheme--headline--unZqjb45 reset WSJTheme--heading-3--2z_phq5h typography--serif-display--ZXeuhS5E ']/a/@href")

    sector = page.xpath("//h2[@class='WSJTheme--headline--unZqjb45 reset WSJTheme--heading-3--2z_phq5h typography--serif-display--ZXeuhS5E ']/parent::div[1]/parent::div[1]/preceding-sibling::div[1]//text()[1]")
    module = [unicodedata.normalize('NFD', i).encode('ascii''ignore').decode("utf-8").replace("\n"," ").replace('\t',""for i in sector if len(i)>1]
    
    article = page.xpath("//h2[@class='WSJTheme--headline--unZqjb45 reset WSJTheme--heading-3--2z_phq5h typography--serif-display--ZXeuhS5E ']/a/text()")
    article = [unicodedata.normalize('NFD', i).encode('ascii''ignore').decode("utf-8").replace("\n"," ").replace('\t',""for i in article]
    
    with open("wsjarticlelist_test.csv",'a'as h:
        k = csv.writer(h)
        for i in range(len(href)):
            k.writerow([article[i], module[i], href[i], date])

    return(page)

def pagenation(page):
    next_page = page.xpath("//span[contains(text(), 'Next Page')]")
    return(next_page)

for line in f:
    daylink = line.strip()
    date = daylink.split("/archive/")[-1]
    page = parsearticle(date, daylink)
    next_page = pagenation(page)
    pagenum = 1
    while next_page:
        pagenum +=1
        print(pagenum)
        newdaylink = daylink + "?page=%s"%pagenum
        page = parsearticle(date, newdaylink)
        next_page = pagenation(page)
        time.sleep(1)

2.3 文章列表

2.4 文章年份分布

YearArticleNum
1997171
199845953
199948813
200051485
200146691
200242997
200338974
200439071
200540219
200641779
200744754
200851897
200954993
201066245
201170842
201266032
201364420
201471768
201566051
201674229
201759907
201849528
201936292
202035819
202112011
Total1220941

2.5 文章主题分布

排除空白主题,1997-2021年共有 5822 个不同的主题。这里列出使用次数超出 10,000 的主题。

SectorArticleNum
Business60119
Markets34663
U.S.23402
Heard on the Street23340
Major Business News22598
Letters21531
Politics20991
Tech Center18511
Earnings18178
Technology17346
U.S. Business News17109
Europe15134
Economy14432
Photos14290
Review & Outlook13759
Business and Finance - Europe13530
Commentary13428
Health12979
Business and Finance - Asia12723
Asia11163
Bookshelf11128
World10833
Media & Marketing10629
Asian Business News10428
Commodities10159
Tech10153

主题中包含中国的文章有

SectorArticleNum
China 20081
China 20091
Snow in China1
China Stocks1
China's Changing Workforce1
China's Money Trail2
China's Rising Risks4
China: The People's Republic at 5012
My China12
China Trade Breakthrough19
China's World79
China Circuit86
Chinas World127
China News1101
China1875
Total3322

3. 爬取文章内容

3.1 分析网页

从网页中获取所有文字信息并不难,但是 WSJ 文章会在文章中插入超链接,如果不做处理的话,爬下来的文字会有很多不符合阅读习惯的换行。我做的处理有:

  • 使用函数 translist(infolist) 筛掉不必要的空格和换行
  • 没有采用直接获取符合条件 html element 下的所有文字的方法,而是对每个 element 进行遍历进行更加精细的筛选。这样做的速度稍微慢一点,但是基本上能呈现比较好的视觉呈现效果。

3.2 爬取文章代码

import time
from lxml import etree
import csv
import re
from tqdm import tqdm
import requests
import json
import pandas as pd
import csv
import unicodedata
from string import punctuation

df = pd.read_excel("/Users/mengjiexu/Dropbox/wsj0512/wsj0513.xlsx",header=0)

headers = {'User-Agent''Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:88.0) Gecko/20100101 Firefox/88.0',
"content-type""application/json; charset=UTF-8",
"Connection""keep-alive"
}

def translist(infolist):
    out = list(filter(lambda s: s and (type(s) != str or len(s.strip()) > 0), [i.strip() for i in infolist]))
    return(out)

def parsearticle(title, date, articlelink):
    with open("wsjcookies.txt""r")as f:
        cookies = f.read()
        cookies = json.loads(cookies)
    session = requests.session()
    data = session.get(articlelink, headers=headers, cookies = cookies)
    time.sleep(1)
    
    page = etree.HTML(data.content)

    arcontent = title + '\n\n' + date +'\n\n'

    content = page.xpath("//div[@class='article-content  ']//p")
    for element in content:
        subelement = etree.tostring(element).decode()
        subpage = etree.HTML(subelement)
        tree = subpage.xpath('//text()')
        line = ''.join(translist(tree)).replace('\n','').replace('\t','').replace('  ','').strip()+'\n\n'
        arcontent += line

    return(arcontent)


for row in tqdm(df.iterrows()):
    title = row[1][0].replace('/','-')
    articlelink = row[1][2]
    date = row[1][3].replace('/','-')
    arcontent = parsearticle(title, date, articlelink)
    with open("/Users/mengjiexu/Dropbox/articles/%s_%s.txt"%(date,title),'w'as g:
        g.write(''.join(arcontent))

3.3 爬取文章样例

4. 翻译

英语阅读速度比较慢的朋友可以调用 百度 API  对文章进行翻译,这样可以一目十行快速提取大量文章信息。为了提高翻译速度,最好整篇文章作为一个文字整体翻译。

4.1 翻译文章代码

import os
import requests
import random
import json
from hashlib import md5
from tqdm import tqdm

file_list = os.listdir("/Users/mengjiexu/Dropbox/articles/")

# Set your own appid/appkey.
appid = 'xxx'
appkey = 'xxx'

# For list of language codes, please refer to `https://api.fanyi.baidu.com/doc/21`
from_lang = 'en'
to_lang =  'zh'

endpoint = 'http://api.fanyi.baidu.com'
path = '/api/trans/vip/translate'
url = endpoint + path

# Generate salt and sign
def make_md5(s, encoding='utf-8'):
    return md5(s.encode(encoding)).hexdigest()

salt = random.randint(3276865536)
headers = {'Content-Type''application/x-www-form-urlencoded'}

def trans(query):

 sign = make_md5(appid + query + str(salt) + appkey)

 # Build request
 payload = {'appid': appid, 'q': query, 'from': from_lang, 'to': to_lang, 'salt': salt, 'sign': sign}

 # Send request
 r = requests.post(url, params=payload, headers=headers)
 result = r.json()

 # Show response
 rusult = json.dumps(result, indent=4, ensure_ascii=False)

 return(result["trans_result"][0]["dst"])

for file in tqdm(file_list):
 content =open("/Users/mengjiexu/Dropbox/articles/%s"%file, 'r').read()
 print(trans(content.strip()))

4.2 翻译文章样例

5. 参考文献

  • Zhu, Christina. 2019. “Big Data as a Governance Mechanism.” The Review of Financial Studies 32 (5): 2021–61. -PDF-.
  • Katona, Zsolt, Marcus Painter, Panos N. Patatoukas, and Jean Zeng. 2018. “On the Capital Market Consequences of Alternative Data: Evidence from Outer Space.” SSRN Scholarly Paper ID 3222741. Rochester, NY: Social Science Research Network. -PDF-.
  • Mukherjee, Abhiroop, George Panayotov, and Janghoon Shon. 2021. “Eye in the Sky: Private Satellites and Government Macro Data.” Journal of Financial Economics, March. -PDF-.

6. 相关推文

Note:产生如下推文列表的 Stata 命令为:
lianxh 爬虫 爬取
安装最新版 lianxh 命令:
ssc install lianxh, replace

  • 专题:文本分析-爬虫
    • Stata爬虫:爬取地区宏观数据
    • Stata爬虫:爬取A股公司基本信息
    • Python:爬取东方财富股吧评论进行情感分析
    • Stata爬虫-正则表达式:爬取必胜客
    • Python爬虫: 《经济研究》研究热点和主题分析
  • 专题:Python-R-Matlab
    • Python:爬取巨潮网公告
    • Python:爬取上市公司公告-Wind-CSMAR
    • Python 调用 API 爬取百度 POI 数据小贴士——坐标转换、数据清洗与 ArcGIS 可视化
    • Python 调用 API 爬取百度 POI 数据
    • Python: 批量爬取下载中国知网(CNKI) PDF论文


尊敬的老师 / 亲爱的同学们:

连享会致力于不断优化和丰富课程内容,以确保每位学员都能获得最有价值的学习体验。为了更精准地满足您的学习需求,我们诚挚地邀请您参与到我们的课程规划中来。 请您在下面的问卷中,分享您 感兴趣的学习主题或您希望深入了解的知识领域 。您的每一条建议都是我们宝贵的资源,将直接影响到我们课程的改进和创新。 我们期待您的反馈,因为您的参与和支持是我们不断前进的动力。感谢您抽出宝贵时间,与我们共同塑造更加精彩的学习旅程!https://www.wjx.cn/vm/YgPfdsJ.aspx# 再次感谢大家宝贵的意见!




New! Stata 搜索神器:lianxh 和 songbl  GIF 动图介绍
搜: 推文、数据分享、期刊论文、重现代码 ……
👉 安装:
  . ssc install lianxh
  . ssc install songbl
👉  使用:
  . lianxh DID 倍分法
  . songbl all



🍏 关于我们

  • 连享会 ( www.lianxh.cn,推文列表) 由中山大学连玉君老师团队创办,定期分享实证分析经验。
  • 直通车: 👉【百度一下: 连享会】即可直达连享会主页。亦可进一步添加 「知乎」,「b 站」,「面板数据」,「公开课」 等关键词细化搜索。


连享会
连玉君老师团队分享,主页:lianxh.cn。白话计量,代码实操;学术路上,与君同行。
 最新文章