11 月看到 httr2 发布了 1.0.0 大版本,包的介绍是:
httr2 (pronounced hitter2) is a ground-up rewrite of httr that provides a pipeable API with an explicit request object that solves more problems felt by packages that wrap APIs (e.g. built-in rate-limiting, retries, OAuth, secure secrets, and more).
httr2(发音为 hitter2)是 httr 从零开始的重写版本,提供了一个可使用管道的 API,并具有显式请求对象,解决了许多围绕 API 的包(例如内置速率限制、重试、OAuth、安全秘密等)所遇到的问题。
看了 Hadley 写的介绍觉得挺好使,尤其是 req_perform_iterative()
从前一个响应生成下一个请求,直到回调函数返回 NULL
或执行了最大请求数。在之前和 elastic 打交道的时候,翻页取数据都是用 Python 处理的,如今似乎可以用 R 更方便地完成。
现在暂时用不到这么高级的功能,于是乎拿起自己的博客试一试构建请求和处理响应。
library(httr2)
构建请求
req = request("https://shitao5.org/")
通过 req_dry_run()
查看 httr2 将要发送给服务器的请求内容,但实际上并不会真的发送请求。
req %>% req_dry_run()
## GET / HTTP/1.1
## Host: shitao5.org
## User-Agent: httr2/1.0.0 r-curl/5.1.0 libcurl/8.3.0
## Accept: */*
## Accept-Encoding: deflate, gzip
我的目标是把博客日志页上的内容摘下来,所以需要构建对日志页的请求:
req_posts = req %>% req_url_path("/posts")
req_posts %>% req_dry_run()
## GET /posts HTTP/1.1
## Host: shitao5.org
## User-Agent: httr2/1.0.0 r-curl/5.1.0 libcurl/8.3.0
## Accept: */*
## Accept-Encoding: deflate, gzip
发送请求,获取响应
req_perform()
即可:
resp = req_posts %>% req_perform()
查看请求内容
查看原始响应
resp_raw()
用于查看从服务器接收到的响应:
# 内容过多不展示
resp %>% resp_raw()
提取响应中的信息
提取响应中 body 部分
resp_body = resp %>% resp_body_html()
resp_body
## {html_document}
## <html lang="en-us">
## [1] <head>\n<meta http-equiv="Content-Type" content="text/html; charset=UTF-8 ...
## [2] <body class=" posts">\n <div class="crop-h"></div>\n<div class="crop-v ...
获取博客日期
这时候该操起 rvest 了。
library(rvest)
dates = resp_body %>%
html_elements("li") %>%
html_element("span") %>%
html_text()
dates %>% head()
## [1] "2023-12-06" "2023-12-02" "2023-12-02" "2023-11-15" "2023-11-14"
## [6] "2023-11-11"
获取博客标题
titles = resp_body %>%
html_elements("li") %>%
html_element("a") %>%
html_text()
titles %>% head()
## [1] "一个阅读中发现的困惑" "学 R 第五年" "骑行路上的老奶奶"
## [4] "读《劳动法》" "一则反思:首先表达关怀" "读《被讨厌的勇气》"
获取博客链接
links = resp_body %>%
html_elements("li") %>%
html_elements("a") %>%
html_attr("href") %>%
paste0("https://shitao5.org", .) # 拼上首页网址
links %>% head()
## [1] "https://shitao5.org/posts/reading-issue/"
## [2] "https://shitao5.org/posts/r5/"
## [3] "https://shitao5.org/posts/cycling-grandma/"
## [4] "https://shitao5.org/posts/labor-law/"
## [5] "https://shitao5.org/posts/care-others/"
## [6] "https://shitao5.org/posts/btydyq/"
汇总提取信息
library(tidyverse)
blog_posts = tibble(
title = titles,
date = ymd(dates),
link = links
)
# 去除 link 列,既方便输出,又可以隐藏俺乱写 slug 的真相😊
blog_posts %>% select(-link)
## # A tibble: 169 × 2
## title date
## <chr> <date>
## 1 一个阅读中发现的困惑 2023-12-06
## 2 学 R 第五年 2023-12-02
## 3 骑行路上的老奶奶 2023-12-02
## 4 读《劳动法》 2023-11-15
## 5 一则反思:首先表达关怀 2023-11-14
## 6 读《被讨厌的勇气》 2023-11-11
## 7 手机换新 2023-11-05
## 8 洗链条 2023-11-04
## 9 I my bike 维修记 2023-10-29
## 10 回家吃饭 2023-10-29
## # ℹ 159 more rows
博客更新分析
每天更新数量
day_n = blog_posts %>% count(date)
day_n %>% arrange(desc(date))
## # A tibble: 151 × 2
## date n
## <date> <int>
## 1 2023-12-06 1
## 2 2023-12-02 2
## 3 2023-11-15 1
## 4 2023-11-14 1
## 5 2023-11-11 1
## 6 2023-11-05 1
## 7 2023-11-04 1
## 8 2023-10-29 2
## 9 2023-10-26 1
## 10 2023-10-24 1
## # ℹ 141 more rows
2022-2023 年更新情况
分析 2022 和 2023 年每天的发文情况:
year_dates = seq.Date(as.Date("2022-01-01"), as.Date("2023-12-31"), by = "day")
year_day = tibble(date = ymd(year_dates)) %>%
left_join(day_n, join_by(date)) %>%
replace_na(list(n = 0))
library(echarts4r)
year_day %>%
mutate(year = year(date)) %>%
group_by(year) %>%
e_charts(date) %>%
e_calendar(range = "2022", top = "40") %>%
e_calendar(range = "2023", top = "260") %>%
e_heatmap(n, coord_system = "calendar") %>%
e_visual_map(max = max(year_day$n))
果然还是周末写得多。