用户
搜索
  • TA的每日心情
    开心
    2021-8-28 22:31
  • 签到天数: 221 天

    连续签到: 1 天

    [LV.7]常住居民III

    i春秋作家

    Rank: 7Rank: 7Rank: 7

    15

    主题

    48

    帖子

    2686

    魔法币
    收听
    0
    粉丝
    5
    注册时间
    2018-6-2

    i春秋签约作者

    发表于 2021-8-7 14:57:58 31681
    本帖最后由 dll_s 于 2021-8-7 15:14 编辑

    Freebuf文章本地保存自动化

    本文原创作者dll_s,本文属i春秋原创奖励计划,未经许可禁止转载

    最近逛freebuf的时候发现有些作者写的文章挺好,可以保存到本地方便揣摩学习,于是写了一个自动化的爬虫脚本分享给大家,同时也简单介绍一下整个爬虫代码的创建流程,大家可以自行在此基础上修改(请勿用于非法目的或对服务器造成压力)

    首先访问freebuf网站,并开启burp监听,分享网络流量包。这里以yangyangwithgnu大佬的主页为例,爬取其主页下的文章并保存到本地

    样例链接:https://www.freebuf.com/author/yangyangwithgnu?type=article

    使用语言:python    使用到的主要模块:requests、selenium、beautifulsoup等

    首先需要使用requests模块模拟发送网页请求(这里不对各模块进行扩展介绍,详细可上网搜索或查看官方文档),然后分析文章获取标题及链接,这里就使用beautifulsoup对html标签进行自动化提取。最后,由于需要保存整个内容到本地,包含各类图象,因此采用selenium模拟请求,并保存为mhtml文件形式。保存方式为模拟击键Ctrl+S,输入路径后回车。

    流程分析

    分析作者昵称html元素,用于作为文章文件保存路径的一部分

    author_url='https://www.freebuf.com/author/dayuxiyou?type=article' #作者主页链接
    r=requests.get(author_url) #发送get请求
    soup=BeautifulSoup(r.text,'html.parser') #将response文本转换为BeautifulSoup对象用于后续查找解析
    author_name=next(soup.find(class_='user-name').strings).rstrip() #检索class为'user-name'的标签并提取文字

    fK5EIU.png

    至于文章题目与链接,本来也是想通过BeautifulSoup分析,但后来发现可以直接请求获取json数据,点击下一页按钮,可以用burp截取到以下信息,page参数控制页面编号

    fK5eG4.png
    页面超出范围时的响应

    fK5ZiF.png

    基于响应报文差别获取文章数据

    """这里使用了一种偷懒的判断方式 假定页面数不超过10页"""
    BASE_URL='https://www.freebuf.com'
    for page_num in range(1,10):   
        r=requests.get(BASE+str(page_num)+ENDING)
        if len(r.text)>1000:    #通过响应报文长度判断是否为最后一页
            ls_data=json.loads(r.text)['data']['data_list']
            for data in ls_data:
                ls_article_title.append(data['post_title']) #提取文章名
                ls_article_url.append(BASE_URL+data['url']) #提取文章链接
            else:
                break

    创建browser对象,用于模拟浏览器请求,需要下载对应版本的chromedriver

    CHROME_DRIVER_PATH='D:\software\chromedriver_win32\chromedriver.exe'  #chromedriver路径
    options = webdriver.ChromeOptions()
    options.add_argument('--save-page-as-mhtml')   #启用另存为mhtml页面选项
    browser = webdriver.Chrome(executable_path=CHROME_DRIVER_PATH,chrome_options=options)

    注意一些由于是付费文章,只有开通会员后才能浏览全文,因此如果需要下载的话需要添加自己的cookie用于后续请求

    fK5AaT.png

    browser.get(BASE_URL)
    browser.add_cookie({"name":"Hm_lpvt_cc53db168808048541c6735ce30421f5","value":"Your cookie"})
    browser.add_cookie({"name":"Hm_lvt_cc53db168808048541c6735ce30421f5","value": "Your cookie"})
    browser.add_cookie({"name":"nickname","value":"Your cookie"})
    browser.add_cookie({"name":"phone","value":"Your cookie"})
    browser.add_cookie({"name":"role","value":"Your cookie"})
    browser.add_cookie({"name":"token","value":"Your cookie"})
    browser.add_cookie({"name":"user_pic","value":"Your cookie"})
    browser.add_cookie({"name":"username","value":"Your cookie"})
    browser.get(TARGET_URL)

    创建一个Author对象,用于保存所有我们需要的信息

    class Author(object):
        def __init__(self,author_url):
            ls_article_title=[]  #文章名
            ls_article_url=[]  #文章链接
            self.author_url=author_url #作者专栏链接
            self.author_title='' #作者名
            self.author_path='' #存储路径
            r=requests.get(author_url)
            soup=BeautifulSoup(r.text,'html.parser')
            self.author_title=next(soup.find(class_='user-name').strings).rstrip() 
            self.author_path=os.path.join(BASE_PATH,self.author_title)
            for page_num in range(1,10):
                r=requests.get(BASE+str(page_num)+ENDING)
                if len(r.text)>1000:
                    ls_data=json.loads(r.text)['data']['data_list']
                    for data in ls_data:
                        ls_article_title.append(data['post_title']) 
                        ls_article_url.append(BASE_URL+data['url'])
                else:
                    break
            self.ls_article_title=ls_article_title
            self.ls_article_url=ls_article_url

    接下来需要完成遍历文章链接并保存到本地的操作,通过pyautogui来模拟键盘操作。这里还用到了pyperclip这个库,其主要作用是由于pyautogui不支持输入Unicode字符,因此需要先将中文复制到剪切板之后再粘贴为保存路径。同样还有一个地方就是为什么需要分为save_page_first和save_page两个函数,是因为在实际运行中模拟键盘常常出现差错,因此利用了下Windows自身特性(仅测试了Win10平台),仅需第一次设置保存文件夹后后续文件就会自动选择此路径用于存储,同时大量的time.sleep也是为了降低键盘模拟差错可能以及给予网站充分的加载时间

    def save_page_first(browser,article_title,article_url):
        browser.get(article_url)
        time.sleep(3)    #给予网站充分的加载时间
        pyautogui.hotkey('ctrl', 's')
        save_path = os.path.join(author.author_path,article_title+'.mhtml')
        time.sleep(1)
        pyperclip.copy(save_path)
        time.sleep(1)
        pyautogui.hotkey("ctrl", "v")
        time.sleep(1)
        pyautogui.hotkey('Enter')
        time.sleep(1)
    
    def save_page(browser,article_title,article_url): 
        browser.get(article_url)
        time.sleep(3)  
        pyautogui.hotkey('ctrl', 's')
        time.sleep(1) 
        pyautogui.hotkey('Enter')

    完整代码

    以上对程序的大致流程和关键代码进行了分析,下面就直接贴一下这个脚本吧,注意更改存储路径、chromedrvier路径和用户cookie

    # -*- coding:utf-8 -*-
    #@author i春秋dll_s
    #使用请修改author_url及browser.add_cookie中参数内容
    import requests
    import json
    from bs4 import *
    from selenium import webdriver
    import os
    import pyautogui
    import pyperclip
    import time
    
    author_url='https://www.freebuf.com/author/yangyangwithgnu?type=article' #作者主页链接
    AUTHOR_NAME=author_url[author_url.rfind('/')+1:author_url.rfind('?')]
    BASE_URL='https://www.freebuf.com'
    BASE_PATH='D:\\freebuf_artical' #存储文件夹路径
    CHROME_DRIVER_PATH='D:\software\chromedriver_win32\chromedriver.exe' #chromedriver路径
    BASE='https://www.freebuf.com/fapi/frontend/author/article?page='
    ENDING='&limit=100&name='+AUTHOR_NAME
    
    class Author(object):
        def __init__(self,author_url):
            ls_article_title=[]  #文章名
            ls_article_url=[]  #文章链接
            self.author_url=author_url #作者专栏链接
            self.author_title='' #作者名
            self.author_path='' #存储路径
            r=requests.get(author_url)
            soup=BeautifulSoup(r.text,'html.parser')
            self.author_title=next(soup.find(class_='user-name').strings).rstrip() 
            self.author_path=os.path.join(BASE_PATH,self.author_title)
            for page_num in range(1,10):
                r=requests.get(BASE+str(page_num)+ENDING)
                if len(r.text)>1000:
                    ls_data=json.loads(r.text)['data']['data_list']
                    for data in ls_data:
                        ls_article_title.append(data['post_title']) 
                        ls_article_url.append(BASE_URL+data['url'])
                else:
                    break
            self.ls_article_title=ls_article_title
            self.ls_article_url=ls_article_url
    
    def save_author(author_url):
        author=Author(author_url)  
        #for article_title,article_url in zip(author.ls_article_title,author.ls_article_url):   打印文章标题和链接
            #print(article_title+':'+article_url) 
        if  not os.path.exists(author.author_path): #创建存储路径
            os.makedirs(author.author_path)
        return author
    
    def save_page_first(browser,article_title,article_url):
        browser.get(article_url)
        time.sleep(3)   
        pyautogui.hotkey('ctrl', 's')
        save_path = os.path.join(author.author_path,article_title+'.mhtml')
        time.sleep(1)
        pyperclip.copy(save_path)
        time.sleep(1)
        pyautogui.hotkey("ctrl", "v")
        time.sleep(1)
        pyautogui.hotkey('Enter')
        time.sleep(1)
    
    def save_page(browser,article_title,article_url): 
        browser.get(article_url)
        time.sleep(3)  
        pyautogui.hotkey('ctrl', 's')
        time.sleep(1) 
        pyautogui.hotkey('Enter')
    
    author=save_author(author_url)
    options = webdriver.ChromeOptions()
    options.add_argument('--save-page-as-mhtml')
    browser = webdriver.Chrome(executable_path=CHROME_DRIVER_PATH,chrome_options=options)
    browser.get(BASE_URL)
    browser.add_cookie({"name":"Hm_lpvt_cc53db168808048541c6735ce30421f5","value":"Your cookie"})
    browser.add_cookie({"name":"Hm_lvt_cc53db168808048541c6735ce30421f5","value": "Your cookie"})
    browser.add_cookie({"name":"nickname","value":"Your cookie"})
    browser.add_cookie({"name":"phone","value":"Your cookie"})
    browser.add_cookie({"name":"role","value":"Your cookie"})
    browser.add_cookie({"name":"token","value":"Your cookie"})
    browser.add_cookie({"name":"user_pic","value":"Your cookie"})
    browser.add_cookie({"name":"username","value":"Your cookie"})
    
    save_page_first(browser,author.ls_article_title[0],author.ls_article_url[0])
    for name,url in zip(author.ls_article_title[1:],author.ls_article_url[1:]):
        save_page(browser,name,url)
    
    time.sleep(10)  #等待所有文章保存成功
    browser.close()
    感谢楼主分享
    使用道具 举报 回复
    发表于 2021-8-9 09:49:27
    表哥这波反向操作,有点东西!
    对论坛发展有任何想法
    欢迎+QQ826177911来搞事!
    使用道具 举报 回复
    发表于 2021-8-11 14:19:42
    局长L 发表于 2021-8-9 09:49
    表哥这波反向操作,有点东西!

    使用道具 举报 回复
    发新帖
    您需要登录后才可以回帖 登录 | 立即注册