qq空间说说导出到本地,处理导出后的文件,输出pdf打印成书
关于导出qq空间说说到本地网上已经有许多教程,而本文的关注重点是对导出后的文件进行处理,包括删除图片、视频的超链接;重新编排说说的时间排序;选取适合自己的样式主题,并同比例调整字体、边距等的大小,使其适合在不同大小的纸张上打印;设置纸张大小,添加页眉页脚,内外页边距等。
注意,上述操作基本使用代码实现,需要一定编程基础。
所有的代码都已上传到 qq-twitter-reverse 中。
这是我的B站账号 夭妖爻幺
一、需要安装或使用的工具、网站
- vscode和一些扩展插件
- prince
- qq说说导出工具
- typora 样式主题
一点点 c++ 知识一点点耐心
二、必要工具的安装
由于安装并不是重点,所以这里只做一些必要说明,具体过程请在其他地方搜索,网上有很多资料。
vscode 和需要用到的插件
安装vscode自不必多说,还需安装 Markdown Preview Enhan、Markdown PDF 等,还有其他一些插件这里不做多说明;
Prince
用于将markdown文件导出为pdf,在 官网 上下载安装,这个软件有很丑的ui界面。这之后,注意还要在vscode中设置好prince的路径。
QQ空间说说导出工具
在站长的 个人网站 中下载,安装到浏览器的扩展插件,根据个人需求调整设置。这部分站内站外已经有相当多的教程,请去浏览并跟着操作。
需要注意的是,如果你要下载的说说比较多(比如我有1300多条),建议在设置里修改下读取间隔,读取过快可能导致暂时被禁止读取导致卡住。
三、导出QQ空间说说
安装好扩展后,打开要读取的QQ空间,我这里打开了中学时候的校园墙。在设置里选择你要导出的时间范围,可以全部,我这里为演示只读取了从24年11月到现在(25年2月)的说说。
慢慢等待,如果要下载的说说很多的话得等上好一会,如果下载的说说里有带视频,下载时可能会经常被暂停,需要手动点击继续下载。下载完成后点击打包下载。得到了一个文件夹和一个压缩包。(不知道没有打码会不会有问题)
观察文件结构,打开文件夹里 Messages\images 目录,发现这里有很多的图片视频文件,这些图像会在导出的md文件里被超链接。
下载的压缩包解压,我们需要的md文件就放在Messages文件夹里。注意到同级目录下有个images文件夹,里面默认是空的,将我们刚刚得到的图片文件全部拷贝进去,这个md文件就算是完成了。
在vscode中打开文件夹,打开 Messages.md 文件,进行侧边栏预览,可以看到,说说、评论、图片(我这里并没有拷贝进去所以显示不成功)都已经可以显示
如果只是想要备份说说的话,到这里基本就完成了。但我们还想要做更多:最终目的是输出pdf打印成书,那么图片和视频就不能存在;这个文件里说说是按时间从新往旧排序,但我们希望按从旧往新排序;默认的输出样式并不好看,我们希望按自己的喜好来配置主题样式;既然想要打印成书,那还得有页眉标题、页脚页码等,还需要再考虑到纸张的内外边距等。
这是本文的关注重点。
四、删除 Messages.md文件多余内容
观察这个文件,可以发现有许多的诸如 <div> </div>
的元素,这些就是图片和视频的超链接。
备份打开新文件,ctrl + f 打开搜索,输入如下正则内容,将所有匹配文本删掉。 “评论(0)”看着很碍眼也删了。
这时候再打开预览,已经干净很多。
<div>[\w|\W]*?</div>
<video[\w|\W]*?</video>
> 评论(0)
删除前:
删除后:
五、倒转Messages.md中说说内容的时间排序
观察这个md文件,可以注意到它的结构如下:
# 年
## 月
>
> 发布时间
说说内容
---
> 发布时间
说说内容
---
# 年
## 月
>
> 发布时间
说说内容
---
> 发布时间
说说内容
---
那么就可以编写代码,利用c++的文件流操作和正则匹配,实现时间排序的倒转,然后写进新文件中。 (是写的很丑啦…)
int twitter_reverse()
{
regex twt_pattern{"> [\\d]+"};
regex h1_pattern{"^# "};
regex h2_pattern{"^## "};
ifstream inputFile("Messages copy.md", ios::ate);
if (!inputFile.good()) {
std::cerr << "Error: Failed to open the file." << std::endl;
return 1;
}
vector<string> twitter{};
pair<string, vector<vector<string>>> h2{};
pair<string, vector<pair<string, vector<vector<string>>>>> h1{};
vector<pair<string, vector<pair<string, vector<vector<string>>>>>> buffer{};
char x;
string line{};
if (inputFile.is_open()) {
streampos size = inputFile.tellg();
for (int var = 1; var <= size; var++) {
inputFile.seekg(-var, ios::end);
inputFile.get(x);
if (x == 0) {
continue;
}
if (x == '\n') {
static bool flag = false;
if (flag == true) {
string templine = line;
if (regex_search(templine, twt_pattern)) {
twitter.push_back(templine);
reverse(twitter.begin(), twitter.end());
h2.second.push_back(twitter);
line = {};
twitter = {};
}
else if (regex_search(templine, h2_pattern)) {
h2.first = templine;
h1.second.push_back(h2);
line = {};
twitter = {};
h2 = {};
}
else if (regex_search(templine, h1_pattern)) {
h1.first = templine;
buffer.push_back(h1);
line = {};
twitter = {};
h2 = {};
h1 = {};
}
else {
twitter.push_back(templine);
line = {};
}
}
flag = !flag;
}
else {
line = x + line;
}
x = 0;
}
if (line.size() > 0) {
h1.first = line;
buffer.push_back(h1);
}
}
inputFile.close();
if (inputFile.bad()) {
std::cerr << "Error: An unrecoverable error occurred while reading the file." << std::endl;
return 1;
}
ofstream outputFile("Message reverse.md", ios::out);
if (outputFile.is_open()) {
for (auto item : buffer) {
outputFile << item.first << "\n" << endl;
for (auto item2 : item.second) {
outputFile << item2.first << "\n>" << endl;
for (auto item3 : item2.second) {
for (auto item4 : item3) {
outputFile << item4 << endl;
}
}
outputFile << endl;
}
outputFile << "<div STYLE=\"page-break-after: always;\"></div>" << "\n"
<< endl; // 输出一个HTML标签,用于在PDF中创建分页符,并换行
}
}
else {
std::cout << "Failed to open the file." << std::endl; // 如果文件打开失败,输出错误信息
}
outputFile.close(); // 关闭文件流
return 0;
}
编译运行代码,打开生成的文件,可以看到新的文件中已按时间顺序从旧往新编排。值得注意的还有编码格式,一般大陆windows用户终端的编码格式是gbk,如果运行代码时发现中文乱码,看一下自己的文件编码是 utf-8 还是 gbk,重新按编码保存或打开。这里不多赘述。
至此对 Messages.md 文件的修改就完成了,新的文件名为 Messages reverse.md。
六、主题样式修改
MPE 默认可选的样式主题太少,而且可能不太符合我们的喜好,那么我们就可以自己修改样式文件。
ctrl + shift + p 输入MPE,选择自定义样式(工作区),打开 style.less 文件,默认为空。这就是MPE的样式文件。
我们打开 typora 的 主题网站 ,可以看到这里有许多可选的主题,找一个自己喜欢的主题,我这里选择 mdmdt,下载源代码。
最终我们得到一个css文件:
复制 root 块的全部内容到 style.less 文件中,再添加
.markdown-preview.markdown-preview {}
将 css 文件中其他所有内容复制进 markdown-preview.markdown-preview 块中。
保存一下,此时再打开md文件预览,可以发现主题已经变了。在浏览器中打开后效果如图。由于校园墙只发图片,而我们刚刚把图片都删了,所以我这里显示一片空白。于是我用自己的说说预览了一下
到这里样式主题就修改完成了。
七、纸张样式排版设计
一直到前面还都是在 html 中的预览,而我们的最终目标是输出 pdf 并打印成书。下面就开始对prince的打印属性进行配置。
但首先还是打开 Messages reverse.md,在最前面添加内容:
<div STYLE="page-break-after: always;"></div>
意思是在最开头分页。
打开 style.less,在 markdown-preview.markdown-preview 块中添加内容如下:
font-family: "Microsoft YaHei";
font-size: 16px;
&.prince {
counter-reset: page 0;
@page {
size: A4;
counter-increment: page 1;
//设置边距
margin-top: 2cm;
margin-bottom: 2cm;
margin-inside: 2cm; //内边距
margin-outside: 1cm; //外边距
@bottom {
font-size: 16px;
border-top: 1pt solid #eeeeee;
margin: 10px 0px 30px;
content: counter(page) //打印页码
}
@top {
font-size: 16px;
content: string(doctitle) " " string(doctitle_2) //打印页眉,格式为 年 月
}
}
h1 {
string-set: doctitle content()
}
h2 {
string-set: doctitle_2 content()
}
}
这段代码的大致意思是设置纸张大小,字体大小,页边距,页眉页脚等,具体按自己需求更改。由于是我自己写的所以并不完善,想配置更多请在 prince 官网的 文档 里学习。
打印后的效果如下:
八、同比例调整 style.less 的所有px值
prince 默认打印 A4 纸,如果想打印成书,或许 A5 大小的值更合适。但如果就按上面的配置打印成 A5 纸的话,原先的字体格式、段落格式等并不合适,会显得很怪异。就像这样:
于是我们需要修改 style.less 中的所有 px 值,按比例放大或缩小,这里我选择用代码实现,将所有 px 值乘以 0.5:
int px_halving()
{
ifstream inputFile("style.less", ios::beg);
if (!inputFile.good()) {
std::cerr << "Error: Failed to open the file." << std::endl;
return 1;
}
ofstream outputFile("style_2.less", ios::out);
if (!outputFile.is_open()) {
std::cout << "Failed to open the file." << std::endl; // 如果文件打开失败,输出错误信息
return 1;
}
vector<string> buffer{};
regex px_pattern{"([\\d]*)px"};
string line{};
if (inputFile.is_open()) {
while (getline(inputFile, line)) {
string tempLine = line;
const sregex_iterator end;
for (sregex_iterator iter{cbegin(tempLine), cend(tempLine), px_pattern}; iter != end;
++iter)
{
int numb = stoi((*iter)[1].str());
int numb_2 = (int)(numb * 0.5);
string::iterator pos{begin(tempLine)};
if (numb >= 1000) {
advance(pos, distance<std::string::const_iterator>(pos, (*iter)[1].second - 4));
if (numb_2 >= 1000) {
*(pos) = numb_2 / 1000 % 10 + '0';
}
else {
*(pos) = ' ';
}
*(pos + 1) = numb_2 / 100 % 10 + '0';
*(pos + 2) = numb_2 / 10 % 10 + '0';
*(pos + 3) = numb_2 % 10 + '0';
}
else if (numb >= 100) {
advance(pos, distance<std::string::const_iterator>(pos, (*iter)[1].second - 3));
if (numb_2 >= 100) {
*(pos) = numb_2 / 100 % 10 + '0';
}
else {
*(pos) = ' ';
}
*(pos + 1) = numb_2 / 10 % 10 + '0';
*(pos + 2) = numb_2 % 10 + '0';
}
else if (numb >= 10) {
advance(pos, distance<std::string::const_iterator>(pos, (*iter)[1].second - 2));
if (numb_2 >= 10) {
*(pos) = numb_2 / 10 % 10 + '0';
}
else {
*(pos) = ' ';
}
*(pos + 1) = numb_2 % 10 + '0';
}
else if (numb > 1) {
advance(pos, distance<std::string::const_iterator>(pos, (*iter)[1].second - 1));
*(pos) = numb_2 % 10 + '0';
}
if (numb > 1) {
cout << format("before:{}, after:{}", numb, numb_2) << endl;
cout << "line: " << line << endl;
cout << "tempLine: " << tempLine << "\n" << endl;
}
}
buffer.push_back(tempLine);
}
}
for (auto iter : buffer) {
outputFile << iter << endl;
}
inputFile.close();
outputFile.close(); // 关闭文件流
return 0;
}
编译运行后,生成新的文件 style2.less,记得注意编码问题。将新的文件内容替换进原本的 style.less,此时打印输出就很正常了。
到这里就是本文的全部内容了。如果已经做完的话,就带上你的pdf,去某宝某东找文印店打印吧。
这是我第一次发专栏,可以给我点个赞吗🥺