Go語言實現CSDN文章導出


title: Go語言實現CSDN文章導出
tags: CSDN,Go
author: Clown95


背景

我一直想把在CSDN上面發佈的文章保存到本地,然後遷移到簡書上,但是CSDN比較閉塞的就是不提供遷移接口。所以我只能尋找第三方工具,但是發現要麼已經失效,要麼就是使用不太友好(對我個人而言),所以我用go語言花了半小時自己擼了一個導出工具。

分析

我們先來分析如何獲取到文章信息:

1.獲取文章地址

首先我們通過文章列表獲取文章的地址。
enter description here

<h4 class="">
        <a href="https://blog.csdn.net/yang731227/article/details/103007106" target="_blank">
        <span class="article-type type-1 float-none">原創</span>        Beego脫坑(六)——使用模板獲取數據      </a>
    </h4>

可以發現 每篇的文章地址 在 h4標籤 下面的a 標籤中。

2.獲取文章內容

接着我們獲取文章的具體內容,CSDN的文章類型有兩種,一種是富文本類型,還有一種是markdown。因此我先查看它們顯示的方式是否一樣

首先我查看的是富文本格式的文章,發現內容是在<article class="baidu_pl">標籤裏面:

<article class="baidu_pl">
               <!--忽略文章內容-->
   </article>`

接着我們在找一篇markdown格式編寫的文章查看下,發現內容依然是在<article class="baidu_pl">標籤裏面。

3.查找接口

找到文章內容在什麼標籤內還沒完事,我這人比較喜歡搞事情。因爲我希望能到把博客導成md格式,但是現在網頁上爬取的內容都是html,也就是富文本格式。因此我去尋找go語言 html轉markdown 的庫,但是不幸的是沒有找到。雖然又工具可以幫我完成這份工作,但是我比較懶希望能減少工作就減少一點。

然後我的思維又開始拐彎,如果是md格式的文章,我們在文章的編輯界面不是可以直接獲取到md文本嗎?我在編輯頁面把所有的md文章,都直接保存爲md不就能省很多功夫?

但是接下來我去查看編輯頁面源代碼的時候,沒有發現文章的內容。這時候不要慌!那麼它肯定是通過某個接口來加載數據的,查看下XHR果然發現了一個接口,參數是文章ID。
https://mp.csdn.net/mdeditor/getArticle?id=103007106 (該接口富文本內容已失效)

新的接口爲:

https://blog-console-api.csdn.net/v1/editor/getArticle?id=103007106

以此類推,我再去打開富文本文章的編輯界面,我希望它也是通過接口加載數據的,這樣在進行數據處理的時候我就能夠省很多代碼。然鵝富文本格式並沒有接口數據。

但是我這個人比較軸,我就想試下,我把接口ID改成富文本文章的ID,看看它是否能夠爲我傳來數據,果然有的時候搞開發就要有折騰的精神,我成功得到了文章內容 。
https://mp.csdn.net/mdeditor/getArticle?id=82253319

https://blog-console-api.csdn.net/v1/editor/getArticle?id=82253319
enter description here

兩個數據不一樣的地方就是,如果是md文章,它 markdowncontent 裏面的內容是 md文本,如果是富文本的文章,它markdowncontent屬性就爲空,因此我們在導出文本的時候,如果遇到markdowncontent不爲空就獲取markdowncontent的內容並保存爲.md ,如果遇到爲空的情況,就獲取content內容並保存爲.html

4.整理

現在我們已知 https://blog-console-api.csdn.net/v1/editor/getArticle?id=xxxx
這個接口可以獲取到文章的信息,參數是文章ID。
所以接下來我們需要做的就是,通過爬蟲模擬csdn登錄狀態,獲取所爬的博客中每篇文章的ID,傳遞給接口,獲取文章標題和內容,並根據格式保存不同的文件。

實現代碼

使用到的第三方庫

  • 使用goconfig庫,來加載配置信息,用來獲取博客地址、文章列表頁數和Cookie。
  • 使用goquery 進行爬蟲

model.go

package models

import (
	"CSDN/utils"
	"github.com/Unknwon/goconfig"
)

var ArrDetailID []string //保存文章ID

var BlogUrl string // 配置博客地址
var Cookie string  // 配置Cookie
var TotalPage int  // 博客文章列表總頁數

type Article struct { //用來解析json
	Data struct {
		//	ID              string `json:"id"`
		Title string `json:"title"`
		//	Articleedittype int    `json:"articleedittype"`
		//	Description     string `json:"description"`
		Content         string `json:"content"`
		Markdowncontent string `json:"markdowncontent"`
		/*
			Tags string `json:"tags"`
			Categories string `json:"categories"`
			Channel string `json:"channel"`
			Type string `json:"type"`
			Status int `json:"status"`
			ReadType string `json:"readType"`
			UseVipView int `json:"use_vip_view"`
			UseFansView int `json:"use_fans_view"`
			Reason string `json:"reason"`
			ResourceURL string `json:"resource_url"`
			OriginalLink string `json:"original_link"`
			AuthorizedStatus bool `json:"authorized_status"`
			CheckOriginal bool `json:"check_original"`
			SelfRecommend bool `json:"selfRecommend"`
		*/
	} `json:"data"`
}

func init() {

	runpath := utils.GetRunPath()
	cfg, err := goconfig.LoadConfigFile(runpath + "/conf/conf.ini")
	if err != nil {
		panic("沒有加載到配置文件")
	}

	BlogUrl, err = cfg.GetValue("csdn", "blogurl")
	if err != nil {
		panic("blogurl錯誤")
	}

	Cookie, err = cfg.GetValue("csdn", "cookie")
	if err != nil {
		panic("cookie錯誤")
	}
	TotalPage, err = cfg.Int("csdn", "totalpage")
	if err != nil {
		panic("totalpage錯誤")
	}
}

csdn.go

package transaction

import (
	"CSDN/models"
	"encoding/json"
	"github.com/PuerkitoBio/goquery"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
	"time"
)

func GetHtml(url string) *http.Response {
	client := &http.Client{ //要管理HTTP客戶端的頭域、重定向策略和其他設置,創建一個Client
		Timeout: time.Second * 2,
	}
	req, err := http.NewRequest("GET", url, nil) //NewRequest使用指定的方法、網址和可選的主題創建並返回一個新的*Request。

	if err != nil {
		log.Println(err)
	}
	req.Header.Add("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36") //模擬瀏覽器User-Agent
	req.Header.Add("Cookie", models.Cookie)
	resp, err := client.Do(req) //Do方法發送請求,返回HTTP回覆
	if err != nil {
		log.Println(err)
	}
	return resp //返回網頁響應
}

func GetdetailID(resp *http.Response) {
	defer resp.Body.Close()

	dom, err := goquery.NewDocumentFromReader(resp.Body)
	if err != nil {
		log.Fatalln(err)
	}
	dom.Find("h4").Each(func(i int, selection *goquery.Selection) {
		time.Sleep(1 * time.Second) //防止訪問次數過於頻繁
		detailurl, _ := selection.Find("a").Attr("href")
		index := strings.LastIndex(detailurl, "/")
		models.ArrDetailID = append(models.ArrDetailID, detailurl[index+1:])
	})
}

func ParseArticleJson(jsonurl string) (string, string) {
	resp := GetHtml(jsonurl)
	defer resp.Body.Close()
	resp_byte, _ := ioutil.ReadAll(resp.Body)
	respHtml := string(resp_byte)

	var article models.Article
	json.Unmarshal([]byte(respHtml), &article)
	title := article.Data.Title
	content := article.Data.Content
	markdown := article.Data.Markdowncontent
	if markdown == "" {
		return title + ".html", content
	} else {

		return title + ".md", markdown
	}

}

utils.go

package utils

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

func WriteWithIoutil(name, content string) {
	data := []byte(content)
	if ioutil.WriteFile(name, data, 0644) == nil {
		fmt.Println("導出成功:", name)
	}
}
// 獲取程序當前運行路徑
func GetRunPath() string{
	file, _ := exec.LookPath(os.Args[0])
	path, _ := filepath.Abs(file)
	index := strings.LastIndex(path, string(os.PathSeparator))
	runpath :=path[:index]
	return  runpath
}

main.go

package main

import (
	"CSDN/models"
	"CSDN/transaction"
	"CSDN/utils"
	"fmt"
	"time"
)

func main() {

	fmt.Println("設置成功,開始導出blog,時間較長請等待!!")
	for i := 1; i <= models.TotalPage; i++ {
		time.Sleep(800) //設置延時
		url := fmt.Sprintf("%s/article/list/%d", models.BlogUrl, i)
		resq := transaction.GetHtml(url)
		transaction.GetdetailID(resq)
	}
	runpath := utils.GetRunPath()

	for i := 0; i < len(models.ArrDetailID); i++ {
		//jsonurl := fmt.Sprintf("https://mp.csdn.net/mdeditor/getArticle?id=%s", models.ArrDetailID[i])
		jsonurl := fmt.Sprintf("https://blog-console-api.csdn.net/v1/editor/getArticle?id=%s", models.ArrDetailID[i])
		name, content := transaction.ParseArticleJson(jsonurl)
		utils.WriteWithIoutil(runpath+"/"+name, content)
		time.Sleep(1000) //設置延時
	}
}

conf.ini 配置文件

[csdn]
blogurl = https://blog.csdn.net/yang731227  ;博客地址
totalpage = 7   ;博客文章列表數
cookie =     ;爬取賬號的cookie
 

導出效果

在這裏插入圖片描述

下載

土豪通道:

  • win: https://download.csdn.net/download/yang731227/12157060
  • mac :https://download.csdn.net/download/yang731227/12154880

或者直接到github上面下載:
https://github.com/Clown95/CSDN-Blog-Export

如果你覺得對你有幫助給個star唄

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章