response = requests.get(url, headers=headers)
        return BeautifulSoup(response.text, “html.parser”)

之后,通过 BeautifulSoup 来解析 HTML 页面,获取文章相关信息,之后,根据相关信息创建文章对象,放到集合中进行返回:
def get_articles(self, url):
         # 存放文章的集合,进行返回
         article_list = []
         beautiful_soup = self.getSoup(url)
         self.log.info(u"开始解析 HTML 页面…")
         articles_div = beautiful_soup.find_all(“div”, class_=“item blog-item”)
         for article_div in articles_div:
             content_div = article_div.find(“div”, class_=“content”)
             header_div = content_div.find(“a”, class_=“header”)
             # 文章URL
             url = header_div[“href”]
             # 文章标题
             title = header_div[“title”]
             # 文章描述
             description_str = content_div.find(“div”, class_=“description”).find(“p”, class_=“line-clamp”)
             if description_str is None:
                 continue
             description = description_str.text
             # 文章阅读数
             read_count_div = content_div.find(“div”, class_=“extra”).find(“div”, class_=“ui horizontal list”).find(“div”, class_=“item”)
             # find_next_sibling 获取兄弟节点
             read_count_i = read_count_div.find_next_sibling(“div”, class_=“item”).find_next_sibling(“div”, class_=“item”)
             read_count = read_count_i.getText()
             # 根据相关信息创建文章对象,放到集合中,进行返回
             __article = Article(title, url, description, read_count)
             article_list.append(__article)self.log.info(u"文章:标题《{0}》,阅读数:{1},链接:{2}".format(title, read_count, url))
return article_list
因为文章的阅读数如果超过 1000 的话,就用 K 来表示,为了在后面筛选指定阅读数的文章,所以需要进行处理,把 K 转换为 1000,代码如下:def handler_read_count(self, article_list):
         “”"
         处理阅读数:把 K 转换为 1000
         :param article_list:文章列表
         “”"
         if article_list is None or len(article_list) == 0:
             self.log.info(u"文章列表为空…")
             return
         for article in article_list:
             read_count_str = article.read_cnt.strip()
             read_count = 0
             if isinstance(read_count_str, str):
                 if read_count_str.endswith(“K”):
                     read_count_str = read_count_str[:-1]  # 去掉K
                     read_count = int(float(read_count_str) * 1000)
                 else:
                     read_count = int(read_count_str)
             article.read_cnt = read_count
接下来就是文章根据阅读数进行筛选和排序了,筛选出阅读数大于等于指定值并且按照阅读数从高到低排序,代码如下:

def get_article_by_read_count_sort(self, article_list, min_read_cnt):
        “”"
        获取大于等于指定阅读数的文章信息, 并按照阅读数从高到低排序
        :param article_list: 文章列表
        :param minx_read_cnt: 最小阅读数
        :return:
        “”"
        if article_list is None or len(article_list) == 0:
            self.log.info(u"文章列表为空…")
            return
        article_list_return = []
        for article in article_list:
            if article.read_cnt >= min_read_cnt:
                article_list_return.append(article)
        # 使用 Lambda 对集合中的对象按照 read_cnt 属性进行排序
        article_list_return.sort(key=lambda Article: Article.read_cnt, reverse=True)
        return article_list_return

以上就可以获取到我们想要的文章信息了,此外,我们可以把信息写到文件文件中,代码如下:

def write_file(self, article_list, file_path):
        # 创建 IO 对象
        file = open(file_path + “/articles.txt”, “a”)

for article in article_list:
            _article_str = str(article)
            file.write(“(” + str(self.file_line_num) + “)” + _article_str)
            file.write(“\n”)
            file.write(“--------------------------------------------------------------------------------------------------------------------------------------------------------”)
            file.write(“\n”)
            # 文件行号
            self.file_line_num += 1 
            time.sleep(0.2)  # 休眠 200 毫秒
        file.close()

之后,把上面的方法整合在一起,代码如下:

def run(self, url, min_read_count):
        # 获取所有文章
        article_list = self.get_articles(url)
        # 对阅读数进行处理
        self.handler_read_count(article_list)
        # 筛选阅读数大于等于指定值,并按阅读数从高到低排序
        _list = self.get_article_by_read_count_sort(article_list, min_read_count)
        # 写文件
        self.write_file(_list, “G:/python”)
        # 打印控制台
        for a in _list:
            self.log.info(a)

main 方法测试一下,地址输入:, 文章阅读数要大于等于1000

if name == ‘main’:
    article = OschinaArticle()
    main_url = “”
    min_read_count = 1000
    article.run(main_url, min_read_count)

控制台日志打印如下:


![]()


写入到文件中的内容如下:


![]()


你以为到这里就完了吗,no, no, no.............,通过上述方式只能获取到首页的文章,如果想获取更多的文章怎么办?开源中国的博客文章列表没有分页,是通过滑动鼠标滚轮来获取更多的页,可是人家的地址导航栏却没有丝毫没有变动,但是可以通过 F12 来看呀,按 F12 后,通过 NetWork 来查看相关的请求和响应情况:


![]()


 


通过滚动几下鼠标滚轮之后,可以发现请求的 URL 还是有规律的:


\_blog\_index\_recommend\_list?classification=428640&p=2&type=ajax  
 \_blog\_index\_recommend\_list?classification=428640&p=2&type=ajax  
 \_blog\_index\_recommend\_list?classification=428640&p=3&type=ajax  
 \_blog\_index\_recommend\_list?classification=428640&p=4&type=ajax  
 \_blog\_index\_recommend\_list?classification=428640&p=5&type=ajax


可以看到除了 p 的参数值不同的话,其他的都相同,p 就是分页标识,p=2就表示第二页,p=3就等于第三页,以此类推,就可以获取到更多的文章啦:

def main(self, min_read_count, page_size):
        # 首页 URL
        self.log.info(“首页##########################”)
        main_url = “”
        self.run(main_url, min_read_count)
        # 第2页到第page_size页
        for page in range(2, page_size):
            self.log.info(“第 {0} 页##########################”.format(str(page)))
            page_url = “_blog_index_recommend_list?classification=428640&p=” + str(page) + “&type=ajax”
            self.run(page_url, min_read_count)
            time.sleep(2)

测试:

if name == ‘main’:
    article = OschinaArticle()
    # 获取到20页的相关文章,并且阅读数要大于等于1000
    article.main(1000, 21)

日志控制台打印如下:


![]()


写到文件中如下:


![]()


可以看到,在 1-20 页中,阅读数大于等 1000 的文章有 114 篇,之后就可以 copy URL 到地址栏进行阅读啦....................


**完整代码如下:**


**OschinaArticle 处理逻辑类:**
# -- coding:utf-8 --
import requests
 from bs4 import BeautifulSoup
 import logging
 import time
 from Article import Article“”"
     爬取开源中国上的文章,且阅读数大于等于1000
 “”"class OschinaArticle:
def init(self):
         self.log = logging
         self.log.basicConfig(level=logging.INFO)
         self.file_line_num = 1def getSoup(self, url):
         “”"
         根据 url 获取 BeautifulSoup 对象
         “”"
         headers = {
             “User-Agent”: “Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.75 Safari/537.36”,
             “Accept”: “text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8”,
             “Host”: “www.oschina.net”}
         response = requests.get(url, headers=headers)
         return BeautifulSoup(response.text, “html.parser”)def get_articles(self, url):
         article_list = []
         beautiful_soup = self.getSoup(url)
         self.log.info(u"开始解析 HTML 页面…“)
         articles_div = beautiful_soup.find_all(“div”, class_=“item blog-item”)
         for article_div in articles_div:
             content_div = article_div.find(“div”, class_=“content”)
             header_div = content_div.find(“a”, class_=“header”)
             # 文章URL
             url = header_div[“href”]
             # 文章标题
             title = header_div[“title”]
             # 文章描述
             description_str = content_div.find(“div”, class_=“description”).find(“p”, class_=“line-clamp”)
             if description_str is None:
                 continue
             description = description_str.text
             # 文章阅读数
             read_count_div = content_div.find(“div”, class_=“extra”).find(“div”, class_=“ui horizontal list”).find(“div”, class_=“item”)
             # find_next_sibling 获取兄弟节点
             read_count_i = read_count_div.find_next_sibling(“div”, class_=“item”).find_next_sibling(“div”, class_=“item”)
             read_count = read_count_i.getText()
             __article = Article(title, url, description, read_count)
             article_list.append(__article)
             # self.log.info(u"文章:标题《{0}》,阅读数:{1},链接:{2}”.format(title, read_count, url))
         return article_listdef handler_read_count(self, article_list):
         “”"
         处理阅读数:把 K 转换为 1000
         :param article_list:文章列表
         “”"
         if article_list is None or len(article_list) == 0:
             self.log.info(u"文章列表为空…")
             return
         for article in article_list:
             read_count_str = article.read_cnt.strip()
             read_count = 0
             if isinstance(read_count_str, str):
                 if read_count_str.endswith(“K”):
                     read_count_str = read_count_str[:-1]  # 去掉K
                     read_count = int(float(read_count_str) * 1000)
                 else:
                     read_count = int(read_count_str)
             article.read_cnt = read_countdef get_article_by_read_count_sort(self, article_list, min_read_cnt):
         “”"
         获取大于等于指定阅读数的文章信息, 并按照阅读数从高到低排序
         :param article_list: 文章列表
         :param minx_read_cnt: 最小阅读数
         :return:
         “”"
         if article_list is None or len(article_list) == 0:
             self.log.info(u"文章列表为空…")
             return
         article_list_return = []
         for article in article_list:
             if article.read_cnt >= min_read_cnt:
                 article_list_return.append(article)
         article_list_return.sort(key=lambda Article: Article.read_cnt, reverse=True)
         return article_list_returndef write_file(self, article_list, file_path):
         file = open(file_path + “/articles.txt”, “a”)for article in article_list:
             _article_str = str(article)
             file.write(“(” + str(self.file_line_num) + “)” + _article_str)
             file.write(“\n”)
             file.write(“--------------------------------------------------------------------------------------------------------------------------------------------------------”)
             file.write(“\n”)
             self.file_line_num += 1
             time.sleep(0.2)  # 休眠 200 毫秒
         file.close()def run(self, url, min_read_count):
         # 获取所有文章
         article_list = self.get_articles(url)
         # 对阅读数进行处理
         self.handler_read_count(article_list)
         # 筛选阅读数大于等于指定值,并按阅读数从高到低排序
         _list = self.get_article_by_read_count_sort(article_list, min_read_count)
         # 写文件
         self.write_file(_list, “G:/python”)
         # 打印控制台
         for a in _list:
             self.log.info(a)def main(self, min_read_count, page_size):
         # 首页 URL
         self.log.info(“首    页##########################”)
         main_url = “”
         self.run(main_url, min_read_count)
         # 第2页到第page_size页
         for page in range(2, page_size):
             self.log.info(“第 {0} 页##########################”.format(str(page)))
             page_url = “_blog_index_recommend_list?classification=428640&p=” + str(page) + “&type=aj