个人博客之类的项目基本上是每个程序员必做的项目了,一般博客完成之后,大家都希望将自己在其他博客系统(如CSDN)上的文章转移至自己的数据库中,如果自己以前的文章过多,一篇篇复制粘贴也太费时间了,这个时候我们可以选择爬虫的方式,将自己的文章爬下来。
如果爬取的是博客主页,爬下来的数据也还是HTML或者普通文本,到时候还要自己处理,最好的当然是爬取自己后台的原始markdown数据了。本文将介绍如何使用Python爬取CSDN后台原始的markdown数据。
博主也是自学不久的爬虫,如有不对的地方希望大佬不吝赐教!
网页解析
我们进入CSDN的后台——内容管理,进入调试模式,打开一篇博客进行编辑,可以发现请求的接口如下:
并且从返回结果中可得如下信息:
其中markdowncontent即是我们的markdown源内容了。
按照基本的爬虫,知道接口、获取请求头这些也就可以开搞了,但是我们实践的时候就会发现,结果不是400就是403,很明显,我们设置的请求头有问题,经过反复验证,发现Request Headers里面有几个内容并非常量。
生成请求变量
以上四个内容,调试js发现x-ca-key 和 x-ca-signature-headers是一个常量,但是x-ca-nonce是每一次请求都需要从新生成的, 而 x-ca-signature 是一个经过x-ca-nonce和url结合的后加密后得到的,因此,我们要按照js的规则去自己生成这两个变量。
def createUuid():
text = ""
char_list = []
for c in range(97,97+6):
char_list.append(chr(c))
for c in range(49,58):
char_list.append(chr(c))
for i in "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx":
if i == "4":
text += "4"
elif i == "-":
text += "-"
else:
text += random.choice(char_list)
return text
def get_sign(uuid,url):
s = urlparse(url)
ekey = "9znpamsyl2c7cdrr9sas0le9vbc3r6ba".encode()
to_enc = f"GET\n*/*\n\n\n\nx-ca-key:203803574\nx-ca-nonce:{uuid}\n{s.path+'?'+s.query[:-1]}".encode()
sign = b64encode(hmac.new(ekey, to_enc, digestmod=hashlib.sha256).digest()).decode()
return sign
createUuid() 和 get_sign() 两个函数,分别对应的是请求头中的x-ca-nonce,x-ca-signature 字段。
获取Cookie
要进入后台,那就需要登录,我们可以通过Cookie的方式。本来想从登录后的响应头中获取Cookie,但最后老是失败。
后面就直接弄了一个登录代码,登陆后获取它的Cookie。
def login():
session = requests.session()
session.cookies = cookielib.LWPCookieJar(filename='cookie/csdn.txt')##将cookie保存至文件
##获取微信登录的二维码的url
response = session.get('https://open.weixin.qq.com/connect/qrconnect?appid=wx0ae11b6a28b4b9fc&scope=snsapi_login&redirect_uri=https%3A%2F%2Fpassport.csdn.net%2Fv1%2Fregister%2FpcAuthCallBack%3FpcAuthType%3Dweixin&state=csdn&login_type=jssdk&self_redirect=default&style=white&href=https://csdnimg.cn/release/passport/history/css/replace-wx-style.css',verify=False)
uuid = re.findall('<img class="qrcode lightBorder" src="(.*?)" />',response.text)[0]
img_url = 'https://open.weixin.qq.com' + uuid
t= show_code(img_url)
t.start()
uuid = uuid.split('/')[-1]
url = 'https://long.open.weixin.qq.com/connect/l/qrconnect?uuid='+uuid
while 1:
print(url)
response = session.get(url,verify=False)
code = re.findall("window.wx_code='(.*?)'",response.text)
print(code)
if code != ['']:
break
time.sleep(1)
url = 'https://passport.csdn.net/v1/register/pcAuthCallBack?pcAuthType=weixin&code=%s&state=csdn' % code[0]
print(url)
session.get(url)
session.cookies.save()
print('登录成功!')
将Cookie保存在txt中:
获取文章ID列表
此部分是通过scrapy来做的(习惯了)。查看网页源码可知,文章ID在如下位置:
def start_requests(self):
urls = [
'https://blog.csdn.net/qq_35524157',
]
for url in urls:
yield scrapy.Request(url=url, callback=self.parse)
def parse(self, response):
blogIdList = response.css('div.article-item-box::attr(data-articleid)').extract()
filename = 'CSDNSpider/spiders/blogIdList.txt'
f = open(filename, "a+")
for i in blogIdList:
print(i)
f.write(i)
f.write('\n')
f.close()
start_requests函数爬取页面,将结果交给parse函数处理。获取到文章ID后保存至文件中。
获取markdown内容
获取单篇文章内容:
def getArticleDetail(url):
uuid = createUuid()
sign = get_sign(uuid,url)
headers = {}
headers['x-ca-key'] = "2038xxx4"
headers['x-ca-nonce'] = uuid
headers['x-ca-signature'] = sign
headers['x-ca-signature-headers'] = "x-ca-key,x-ca-nonce"
session = requests.session()
session.cookies = cookielib.LWPCookieJar(filename='cookie/csdn.txt')
session.cookies.load()
data = session.get(url,headers=headers).json()
return data
用前面写好的createUuid() 和 get_sign() 两个函数生成请求头中的x-ca-nonce,x-ca-signature 字段,读取保存在文件中的Cookie,发起请求。
以上则完成了一篇博客markdown内容的爬取,最后用保存在文件中的文章ID,获取全量的文章:
url = "https://blog.csdn.net/qq_35524157" # 更改为你自己的csdn博客主页地址
article_list = get_all(url)
for article in article_list:
url = "https://bizapi.csdn.net/blog-console-api/v3/editor/getArticle?id=%s&model_type="%article
text = getArticleDetail(url)
markdown = text['data']['markdowncontent']
title = text['data']['title']
try:
if markdown:
for i in ["?", "、", "\\", "/", "*", '"', ":", "<", ">", "|"]:
title = title.replace(i, "-")
print("文章\\%s.md" % title)
with open("文章\\%s.md" % title, 'a',encoding='utf8') as f:
print(markdown)
f.write(markdown)
except Exception as e:
print("error", e)
结果如下:
源码获取
关注下方公众号,回复“CSDN_Spider”,即可获取源码。该公众号将不定期分享一些小demo、小项目以及学习心得。
往期文章: