
New! 搜推文,找资料,用
lianxh
命令:
安装:ssc install lianxh, replace
使用:lianxh 合成控制
lianxh DID + 多期, w


⛳ Stata 系列推文:
- 全部 | Stata入门 | Stata教程 | Stata资源 | Stata命令
- 计量专题 | 论文写作 | 数据分享 | 专题课程
- 结果输出 | Stata绘图 | 数据处理 | Stata程序
- 回归分析 | 面板数据 | 交乘项-调节 | IV-GMM
- 内生性-因果推断 | 倍分法DID | 断点回归RDD | PSM-Matching | 合成控制法
- Probit-Logit | 时间序列 | 空间计量 | 分位数回归 | 生存分析 | SFA-DEA
- 文本分析-爬虫 | Python-R-Matlab | 机器学习
- Markdown | 工具软件 | 其它
作者: 王颖 (四川大学)
邮箱: wangyingchn@outlook.com
目录
[[TOC]]
0. 前言
在执行 Python 任务的过程中,我们通常会想提高程序的效率。尤其在处理大量任务的时候,提高程序的效率可以节省大量的时间。
以数据爬取为例,爬虫的基本流程都是,请求数据 → 解析数据 → 存储数据。如果有一万条数据,这个流程就要重复执行一万次,而且是线性的执行。就像车流一样,如果只有一条车道,一万辆车排队通过就需要一万次。如果有了双车道,就可以减少一半的时间,如果有了四车道,时间可以再减少一半。
那我们在程序运行中,能否想办法多开几条车道呢?在 Python 中,可以通过开启多进程或者多线程,来提高程序运行的效率。我们将对 Python 中的多进程、多线程进行简单的介绍,并主要介绍多线程在 Python 爬虫中的应用。
1. 进程与线程
先简单的介绍一下进程与线程的含义和两者的区别。
进程(Process),是资源单位。
要运行一个程序时,操作系统会分配一部分内存空间和资源来运行这个程序,这就是进程。
比如电脑中运行 Word、Excel、微信、QQ 等,每个程序都是一个进程。
线程(Thread)是执行单元。每个进程要都需要执行,这个执行就由线程来完成。
打个比方,进程就好像一家公司,里面包括各种资源,比如办公室、办公桌、电脑等,
而线程就是公司里的员工,来执行具体的任务。
如果一家公司没有员工,那公司就无法运营。
同理,每个进程中至少会有一个线程。只要启动程序,进程中就默认会有一个主线程。
当然,为了提高效率,一个进程也可能有多个线程去执行(多招几个员工)。
进程和线程之间的简单区别如下表:
进程 | 线程 | |
---|---|---|
简单定义 | 资源单位 | 执行单位 |
资源占用 | 多 | 少 |
资源共享 | 进程间的资源相互独立 | 同一进程下的线程间共享资源 |
互相通信 | 进程间只能间接交流 | 线程间可以直接交流,一个线程可以控制同一进程下的其他线程 |
可靠程度 | 进程间不会互相影响 | 一个线程挂掉会导致整个进程挂掉 |
- 在 Python 实际应用中,更多使用多线程,因为消耗资源更少,且线程间共享内存、变量等,互相交流很容易。
- 与多进程、多线程相关的概念还有全局解释器锁(GIL)、互斥锁、线程守护等
- 还有其他方法也涉及提高程序运行效率,如队列、协程等。
2. Python 中的多进程
我们通过一个简单的虚拟任务,来看看在 Python 中如何使用多进程。以及多进程执行和正常执行在任务耗时上的区别。
# -*- coding: utf-8 -*-
# Author: W.Y.
# Email: wangyingchn@outlook.com
# Date: 2022/4/24
import time # 时间模块
from random import randint # 生成随机数
from multiprocessing import Process # 多进程
# 定义一个虚拟任务
def random_task(name):
task_time = randint(3, 8) # 任务随机执行 3 至 8 秒
time.sleep(task_time)
print(f'{name}执行时间{task_time}s')
# 正常执行两个任务
def main():
start_main = time.time()
random_task('正常执行任务1')
random_task('正常执行任务2')
end_main = time.time()
print(f'正常执行总耗时{end_main - start_main}s')
# 使用多进程执行两个任务
def multi_process():
start_process = time.time()
t1 = Process(target=random_task, args=('多进程执行任务1', )) # 创建新的进程,注意此处 args 必须为元组
t1.start() # 告诉进程可以开始执行,具体执行开始时间由 CPU 决定
t2 = Process(target=random_task, args=('多进程执行任务2', ))
t2.start()
t1.join() # join() 方法,等待进程执行结束再往下执行主程序(为了统计时间)
t2.join()
end_process = time.time()
print(f'多进程执行总耗时{end_process - start_process}s')
if __name__ == '__main__':
print('-'*30)
main()
print('-'*30)
multi_process()
程序运行的结果如下:
------------------------------
正常执行任务1执行时间 7s
正常执行任务2执行时间 5s
正常执行总耗时 12.014511823654175 s
------------------------------
多进程执行任务 2 执行时间 3 s
多进程执行任务1执行时间 5 s
多进程执行总耗时5. 3230578899383545 s
可以看到,正常执行任务时,第一个任务结束后才执行第二个任务,任务的总耗时等于两个任务的耗时总和。而使用多进程时,两个任务可以同时进行,此时任务的总耗时不再是两个任务的耗时总和。可见,使用多进程确实可以提高程序的运行效率。
3. Python 中的多线程
接下来我们来看看 Python 中的多线程。
虽然进程和线程的本质完全不同,但是在 Python 中启用多进程和多线程的方式是一样的,只是需要使用不同的模块。
# -*- coding: utf-8 -*-
# Author: W.Y.
# Email: wangyingchn@outlook.com
# Date: 2022/4/24
import time # 时间模块
from random import randint # 生成随机数
from threading import Thread # 多线程
# 定义一个虚拟任务
def random_task(name):
task_time = randint(3, 8) # 任务随机执行 3 至 8 秒
time.sleep(task_time)
print(f'{name}执行时间{task_time}s')
# 正常执行两个任务
def main():
start_main = time.time()
random_task('正常执行任务1')
random_task('正常执行任务2')
end_main = time.time()
print(f'正常执行总耗时{end_main - start_main}s')
# 使用多线程执行两个任务
def multi_thread():
start_thread = time.time()
t1 = Thread(target=random_task, args=('多线程执行任务1', )) # 创建新的线程,注意此处 args 必须为元组
t1.start() # 告诉线程可以开始执行,具体执行开始时间由 CPU 决定
t2 = Thread(target=random_task, args=('多线程执行任务2', ))
t2.start()
t1.join() # join() 方法,等待线程执行结束再往下执行主程序(为了统计时间)
t2.join()
end_thread = time.time()
print(f'多线程执行总耗时{end_thread - start_thread}s')
if __name__ == '__main__':
print('-'*30)
main()
print('-'*30)
multi_thread()
同样的,从图中的运行结果可以看到,多线程也可以提高程序的运行效率。

4. 多线程在爬虫中的应用
在爬虫任务中使用多进程或多线程可以极大的提高爬虫的效率。通常我们更经常使用多线程来提高效率。因此我们将介绍多线程在爬虫中的应用,多进程的使用是类似的。
4.1 线程池
在前面的任务中,我们通过 Thread()
来开启新的线程,那如果我们想一次性多开启一些线程怎么办呢?
这时可以通过线程池来一次性开辟一些线程,把任务提交给线程池,由线程池来调度任务的具体执行。具体开辟多少个线程可以根据 CPU 的情况来决定。
线程池的基本用法如下:
from concurrent.futures import ThreadPoolExecutor
def func(name):
for _ in range(1000):
print(name, _)
if __name__ == '__main__':
with ThreadPoolExecutor(50) as t: # 一次性开辟 50 个线程
for i in range(100):
t.submit(func, name=f"线程{i}")
print("执行完毕") # with 外面的代码,会在所有线程完成后才执行
4.2 爬取静态网站(历史天气)
接下来,我们用上次 Python 爬虫的两个案例,来看看多进程在爬虫实战中的应用。首先是爬取静态网站历史天气的案例。
在这个例子中,我们通过 generate_urls()
函数一次性生成所有的网址。
正常执行时,我们通过一个 for
循环,逐次的执行爬取任务。使用多线程时,我们通过线程池一次性构造很多线程,然后把爬取任务分配给各个线程去执行。
from concurrent.futures import ThreadPoolExecutor
# 定义爬取函数
def crawler(url, save_path):
response = get_response(url)
results = parse_data(response)
save_data(results, save_path)
print(f'成功爬取数据:{url}')
if __name__ == '__main__':
urls = generate_urls()
save_file = 'weather.csv'
# 正常执行
# for u in urls: # 在网址中循环
# time.sleep(2) # 每次爬取休息 2 秒,以免太过频繁的请求
# crawler(u, save_file) # 进行爬取
# 多线程执行
with ThreadPoolExecutor(5) as t:
for u in urls:
t.submit(crawler, url=u, save_path=save_file) # 传入要执行的函数及其参数
4.3 爬取动态网站( bilibili 视频评论)
同样的,在爬取动态网站 bilibili 视频评论的案例中,我们通过页面翻页循环来执行爬取任务。
正常执行时,我们通过 for
循环逐次翻页进行爬取。而多线程时,我们通过线程池构造多个线程,通过线程池进行多线程爬取。
from concurrent.futures import ThreadPoolExecutor
# 定义爬取函数
def crawler(page, save_path):
response = get_response(page)
comments = parse_data(response)
save_data(comments, save_path)
print(f'成功爬取第{page+1}页')
if __name__ == '__main__':
save_file = 'bilibili.csv'
total_counts = 1000
# 正常执行
# for p in range(total_counts//20 + 1):
# crawler(p, save_file)
# 多线程执行
with ThreadPoolExecutor(5) as t:
for p in range(total_counts//20 + 1):
t.submit(crawler, page=p, save_path=save_file) # 传入要执行的函数及其参数
5. 参考资料
6. 相关推文
Note:产生如下推文列表的 Stata 命令为:
lianxh 爬
安装最新版lianxh
命令:
ssc install lianxh, replace
- 专题:文本分析-爬虫
- 专题:Python-R-Matlab
- Python爬取静态网站:以历史天气为例
- Python爬虫1:小白系列之requests和json
- Python爬虫2:小白系列之requests和lxml
- Python爬虫:爬取华尔街日报的全部历史文章并翻译
- Python爬虫:从SEC-EDGAR爬取股东治理数据-Shareholder-Activism
- Python:爬取巨潮网公告
- Python:爬取上市公司公告-Wind-CSMAR
- Python: 6 小时爬完上交所和深交所的年报问询函
- Python 调用 API 爬取百度 POI 数据小贴士——坐标转换、数据清洗与 ArcGIS 可视化
- Python 调用 API 爬取百度 POI 数据
- Python: 批量爬取下载中国知网(CNKI) PDF论文


[(https://www.lianxh.cn/details/1543.html)
资源共享
- 连享会资料 ……
- 在线视频:lianxh-class.cn
- Stata 33 讲,100 万+ 播放,Stata 入门必备,公开课
- 直击面板数据模型,10 万+ 播放,白话面板模型,公开课
- … more …
- 论文复现和数据
- 主题分类
- 热门推文

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

关于我们
- Stata连享会 由中山大学连玉君老师团队创办,定期分享实证分析经验。more……
- 扫码加入连享会微信群,提问交流更方便
