今天你想爬谁的粉呢? 谁粉多,就爬谁。 那谁有粉? 沉默王二有粉。
今天咱们继续学习 Python 爬虫,从本篇博客开始进行短暂的(15 篇)多线程爬虫学习。
第一篇就要采集 大佬@沉默王二 的粉丝,坐拥 27W+ 读者,属实让人羡慕。
目标数据源分析
本次要抓取的数据源是 https://blog.csdn.net/qing_gee?type=sub&subType=fans ,其中的 ID 可以切换为你希望采集的 ID,当然包括你自己的 ID。
该页面下滑刷新会自动请求一个 API 接口,即 https://blog.csdn.net/community/home-api/v1/get-fans-list?page=3&size=20&noMore=false&blogUsername=qing_gee ,其中参数如下:
page :页码,根据目标人粉丝总数 / 20 计算获取即可;size :每页数据,默认值 20;noMore :无用;blogUsername :博客用户名
同时在测试接口过程中,接口会返回异常数据,实测增加一个延时控制,可以大幅度提高接口数据返回稳定性。
{'code': 400, 'message': 'fail', 'data': None}
正常接口数据返回如下图所示:
使用技术点说明
本次采用 Python 多线程实现数据的采集,编码使用 threading 模块进行多线程控制,本系列专栏从最简单的多线程开始进行学习,例如本例,一次性发起 5(可自定义)个请求。
完整代码如下所示,代码说明请参考注释部分与尾部说明
import threading
from threading import Lock, Thread
import time
import os
import requests
import random
class MyThread(threading.Thread):
def __init__(self, name):
super(MyThread, self).__init__()
self.name = name
def run(self):
global urls
lock.acquire()
one_url = urls.pop()
print("正在爬取:", one_url)
lock.release()
print("任意线程等待随机时间")
time.sleep(random.randint(1,3))
res = requests.get(one_url, headers=self.get_headers(), timeout=5)
if res.json()["code"] != 400:
data = res.json()["data"]["list"]
for user in data:
name = user['username']
nickname = self.remove_character(user['nickname'])
userAvatar = user['userAvatar']
blogUrl = user['blogUrl']
blogExpert = user['blogExpert']
briefIntroduction = self.remove_character(
user['briefIntroduction'])
with open('./qing_gee_data.csv', 'a+', encoding='utf-8') as f:
print(f'{name},{nickname},{userAvatar},{blogUrl},{blogExpert},{briefIntroduction}')
f.write(f"{name},{nickname},{userAvatar},{blogUrl},{blogExpert},{briefIntroduction}\n")
else:
print(res.json())
print("异常数据", one_url)
with open('./error.txt', 'a+', encoding='utf-8') as f:
f.write(one_url+"\n")
def remove_character(self, origin_str):
if origin_str is None:
return
origin_str = origin_str.replace('\n', '')
origin_str = origin_str.replace(',', ',')
return origin_str
def get_headers(self):
uas = [
"Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)"
]
ua = random.choice(uas)
headers = {
"user-agent": ua,
'cookie': 'UserName=你的ID; UserInfo=你的UserInfo; UserToken=你的UserToken;',
"referer": "https://blog.csdn.net/qing_gee?type=sub&subType=fans"
}
return headers
if __name__ == '__main__':
lock = Lock()
url_format = 'https://blog.csdn.net/community/home-api/v1/get-fans-list?page={}&size=20&noMore=false&blogUsername=qing_gee'
urls = [url_format.format(i) for i in range(1, 13300)]
l = []
while len(urls) > 0:
print(len(urls))
for i in range(5):
p = MyThread("t"+str(i))
l.append(p)
p.start()
for p in l:
p.join()
代码运行结果如下图所示:
上述代码用到了多线程,也同时用到了线程锁,简单的多线程代码可以抽象为下述内容。
简单的多线程代码:
import threading
import time
def run(n):
print('task', n)
time.sleep(3)
if __name__ == '__main__':
t1 = threading.Thread(target=run, args=('t1',))
t2 = threading.Thread(target=run, args=('t2',))
t1.start()
t2.start()
其中比较核心的代码是 threading.Thread ,参数 target 后面的值是函数名,args 是传递的参数,注意必须为元组类型。
爬虫代码还是用了共享全局变量,简化代码如下所示,其中重点学习 lock=Lock() 部分代码,以及在使用全局变量前后的 lock.acquire() 和 lock.release() 。其中还用到了线程的 join 方法,该方法主要是为了让主线程等待子线程执行。
import threading
from threading import Lock,Thread
import time,os
def work():
global urls
lock.acquire()
one_url = urls.pop()
lock.release()
print("得到的 URL 为",one_url)
if __name__ == '__main__':
lock = Lock()
url_format = 'https://blog.csdn.net/community/home-api/v1/get-fans-list?page={}&size=20&noMore=false&blogUsername=qing_gee'
urls = [url_format.format(i) for i in range(1, 13300)]
l = []
for i in range(3):
p = Thread(target=work)
l.append(p)
p.start()
for p in l:
p.join()
拿到这些数据,可以针对性的去描述一个作者的用户画像了,本部分在后续的博客中为大家单独开一篇详细介绍。
代码在数据清理部分,还有优化的空间,由于设置了 13300 页数据,故最终抓取到 26W+数据,查询了一下,存在 梦想橡皮擦 。
关注者中至少有 83 位博客专家,可以看到博客专家的个人简介写的都比较清楚,同时发现 jiangtao (CSDN 创始人)
收藏时间
代码下载地址:https://codechina.csdn.net/hihell/python120,可否给个 Star。
260000+ 粉丝数据,你可以直接花钱省时间,数据有分析价值,还可继续深挖
来都来了,不发个评论,点个赞,收个藏吗?
今天是持续写作的第 202 / 365 天。 可以关注我,点赞我、评论我、收藏我啦。
更多精彩
《Python 爬虫 100 例》只需要 39.9 元,即可享受 100+篇博客阅读权,每篇不到 4 毛钱。
|