作者: James

  • 突破Discuz!的防采集

    突破Discuz!的防采集

    当短时间内频繁采集discuz,会触发discuz的防采集机制,默认打开的是一段javascript脚本,然后再通过跳转跳转到真实地址,这样子的情况下如何采集呢,本文将教会你如何破解这类型的防采集的采集。

    Discuz开启防采集后的采集方法,也就是帖子后面加了?dsign=这样子的

    Discuz在X3中增加了防采集功能,具体见Discuz x3.0防采集设置图文教程

    [b2_insert_post id=”558″]

    开启防采集后,访问DZ站点的伪静态链接如http://www.discuz.net/thread-3275423-1-1.html后面会跟上一个?_dsign=xxxxxx,正常链接如http://www.discuz.net/forum.php?mod=viewthread&tid=3305274会加上&_dsign=xxxxxx。这让人很不爽 (o#゜ 曲゜)o

    使用httplib2访问原帖子页面(下面以http://www.dz.net/forum.php?mod=viewthread&tid=768为例),返回的是一段混淆过的js,如:

    <script type="text/javascript">RKbW=function(){'RKbW';var _R=function(){return '=76'}; return _R();};function AIV(AIV_){function _A(AIV_){function ph(){return getName();}function AIV_(){}return ph();return AIV_}; return _A(AIV_);}DmZP='iew';_IX161 = 'assign';function zoQ(zoQ_){function ti(){return getName();};return ti();return 'zoQ'}function r2oe(){'return r2oe';return 'ad&'}_eloda = 'replace';F59s=function(){'return F59s';return 'p?m';};HP=function(){'return HP';return 'n';};function getName(){var caller=getName.caller;if(caller.name){return caller.name} var str=caller.toString().replace(/[\s]*/g,"");var name=str.match(/^function([^\(]+?)\(/);if(name && name[1]){return name[1];} else {return '';}}uM=function(){'return uM';return '9';};eG9=function(eG9_){'return eG9';return eG9_;};function kp(kp_){function _k(kp_){function o(){return getName();}function kp_(){}return o();return kp_}; return _k(kp_);}vD='1';BN=function(){'BN';var _B=function(){return 'r'}; return _B();};HALw='rum';_RZnE9 = 'href';o5y=function(o5y_){'return o5y';return o5y_;};function PH(){'return PH';return '.'}_BDkwZ = location;function w2(){'return w2';return '_'}KTI4=function(){'return KTI4';return '910';};_NUuAJ = window;wX=function(){'wX';var _w=function(){return 'd'}; return _w();};iyL=function(iyL_){'return iyL';return iyL_;};location.replace((function(){'return Q8mM';return '/fo'})()+HALw+PH()+AIV('Gs8')+F59s()+kp('nm')+(function(){'return njFH';return (function(){return 'd=v';})();})()+DmZP+eG9('th')+BN()+(function(){'return XD';return 'e'})()+r2oe()+zoQ('yKM')+wX()+RKbW()+'8&'+w2()+o5y('ds')+iyL('ig')+HP()+(function(){'return l26W';return '=6f'})()+uM()+(function(){'return by';return (function(){return '7';})();})()+KTI4()+vD);_NUuAJ['href']=(function(){'return Q8mM';return '/fo'})()+HALw+PH()+AIV('Gs8')+F59s()+kp('nm')+(function(){'return njFH';return (function(){return 'd=v';})();})()+DmZP;</script>

    显然这样人类是无法理解的……不过使用notepad++的JSFormat插件格式化后还是能看懂的嗯-v-

    实际上这是一个字符串替换然后重定向的脚本,生成原理如下(不完全按照上面的例子):

    • 原始字符串为location.href=forum.php?mod=viewthread&tid=768&_dsign=6f979101
    • 随机分割这个这个串,如分成l, oc, a, tio, n., ……
    • 对每一个子串替换成一个随机命名的函数,如l替换成_Oc9S(),则在脚本里加一句function _Oc9S(){return ‘l’;}以此类推
    • 每个function都可能加入奇怪的例如’return l;’这样毫无意义的混淆
    • 最后在末尾加上window.href=yyy,yyy为取forum.php?mod=viewthread&tid=768&_dsign=6f979101的前x个字符后得到一个子串

    我大概写得不清楚……就……这么个意思……

    [content_hide]

    毫无疑问要使用PyV8了,本来想自己写个解释器,但是算法太渣只能呜呼哀哉QAQ
    这里有个问题,PyV8只是一个js解释器的包装,不是浏览器的js运行时,因此location啊window啊都是undefined的,所以要手动把这些都去掉。(有一点要说明,就是由于js很骚,location.href有时会被写成location[‘href’],或者用location.assign或者location.replace,而assign和replace都可能被一个随机命名的变量替换掉了)

    dz-anti-anti-crawler

    直接看代码吧

    import PyV8
    import re
    js='''<script type="text/javascript">.........</script>'''
    #去掉<script>标签
    js=js[31:-9]
    for st in ['window','location',"'assign'","'href'","'replace'"]:
    equal=re.findall('[_A-Za-z0-9 =]+%s;'%st,js)#找到变量赋值等式
    if equal==[]:#有可能没有
    continue
    else:
    equal=equal[0]
    var=equal.split('=')[0].strip()#找出变量名
    #把等式干掉
    js=js.replace(equal,'')
    #把变量替换成它真正的意思
    js=js.replace(var,st)
    #把['xx'] 替换成 .xx
    js=js.replace("['%s']"%st.strip("'"),'.%s'%st.strip("'"))
    #将 window.href= 后的内容踢掉,因为当PyV8只输出最后一个等式的值
    if re.findall('window\.href=.+',js)!=[]:
    js=js.replace(re.findall('window\.href=.+',js)[0],'')
    #删掉location.xxx=
    js=js.replace('location.href=','').replace('location.replace','').replace('location.assign','')
    #交给你了-v-
    ctxt2 = PyV8.JSContext()
    ctxt2.enter()
    print ctxt2.eval(js)

    这样就得到了包含dsign的新url,可以继续爬了

    最后对DZ的防采集再说几句

    1. 防采集是对IP不对用户的
    2. 而且要看站长的设置,有可能只对帖子或日志等开启;你们可以到Discuz x3.0防采集设置图文教程感受一下
    3. _dsign的值是固定的,计算完一次js后可以考虑保存起来,以后可以直接访问加上_dsign的url
    4. 防采集是针对不支持js的机器人的,如果用python直接控制浏览器的话,可以无视之
    5. 如果mechanize和PyV8能合体那该多好~ o(* ̄▽ ̄*)o

    [/content_hide]

  • 爬虫神器Selenium全攻略

    爬虫神器Selenium全攻略

    今天带大家一起学(复)习模拟浏览器运行的库Selenium,它是一个用于Web应用程序测试的工具。Selenium测试直接运行在浏览器中,就像真正的用户在操作一样。支持的浏览器包括IE(7, 8, 9, 10, 11),Mozilla FirefoxSafariGoogle ChromeOperaEdge等。

    这里我将以Chrome为例进行Selenium功能的演示~

    0. 准备工作

    在开始后续功能演示之前,我们需要先安装Chrome浏览器并配置好ChromeDriver,当然也需要安装selenium库!

    0.1. 安装selenium库

    pip install selenium

    0.2. 安装浏览器驱动

    其实,有两种方式安装浏览器驱动:一种是常见的手动安装,另一种则是利用第三方库自动安装。

    以下前提:大家都已经安装好了Chrome浏览器哈

    手动安装

    先查看本地Chrome浏览器版本:(两种方式均可)

    • 在浏览器的地址栏键入Chrome://version,即可查看浏览器版本号
    • 或者点击Chrome菜单 帮助关于Google Chrome,查看浏览器版本号

    再选择对应版本号的驱动版本

    下载地址:https://chromedriver.storage.googleapis.com/index.html

    最后进行环境变量配置,也就是将对应的ChromeDriver的可执行文件chromedriver.exe文件拖到PythonScripts目录下。

    注:当然也可以不这样做,但是在调用的时候指定chromedriver.exe绝对路径亦可。

    自动安装

    自动安装需要用到第三方库webdriver_manager,先安装这个库,然后调用对应的方法即可。

    from selenium import webdriver
    from selenium.webdriver.common.keys import Keys
    from webdriver_manager.chrome import ChromeDriverManager
    
    browser = webdriver.Chrome(ChromeDriverManager().install())
    
    browser.get('http://www.baidu.com')
    search = browser.find_element_by_id('kw')
    search.send_keys('python')
    search.send_keys(Keys.ENTER)
    
    # 关闭浏览器
    browser.close()

    在上述代码中,ChromeDriverManager().install()方法就是自动安装驱动的操作,它会自动获取当前浏览器的版本并去下载对应的驱动到本地。

    ====== WebDriver manager ======
    Current google-chrome version is 96.0.4664
    Get LATEST chromedriver version for 96.0.4664 google-chrome
    There is no [win32] chromedriver for browser  in cache
    Trying to download new driver from https://chromedriver.storage.googleapis.com/96.0.4664.45/chromedriver_win32.zip
    Driver has been saved in cache [C:\Users\Gdc\.wdm\drivers\chromedriver\win32\96.0.4664.45]

    如果本地已经有该浏览器渠道,则会提示其已存在。

    ====== WebDriver manager ======
    Current google-chrome version is 96.0.4664
    Get LATEST driver version for 96.0.4664
    Driver [C:\Users\Gdc\.wdm\drivers\chromedriver\win32\96.0.4664.45\chromedriver.exe] found in cache

    搞定以上准备工作,我们就可以开始本文正式内容的学习啦~

    1. 基本用法

    这节我们就从初始化浏览器对象、访问页面、设置浏览器大小、刷新页面和前进后退等基础操作。

    1.1. 初始化浏览器对象

    在准备工作部分我们提到需要将浏览器渠道添加到环境变量或者指定绝对路径,前者可以直接初始化后者则需要进行指定。

    from selenium import webdriver
    
    # 初始化浏览器为chrome浏览器
    browser = webdriver.Chrome()
    
    # 指定绝对路径的方式
    path = r'C:\Users\Gdc\.wdm\drivers\chromedriver\win32\96.0.4664.45\chromedriver.exe'
    browser = webdriver.Chrome(path)
    
    # 关闭浏览器
    browser.close()

    可以看到以上是有界面的浏览器,我们还可以初始化浏览器为无界面的浏览器

    from selenium import webdriver
    
    # 无界面的浏览器
    option = webdriver.ChromeOptions()
    option.add_argument("headless")
    browser = webdriver.Chrome(options=option)
    
    # 访问百度首页
    browser.get(r'https://www.baidu.com/')
    # 截图预览
    browser.get_screenshot_as_file('截图.png')
    
    # 关闭浏览器
    browser.close()

    完成浏览器对象的初始化后并将其赋值给了browser对象,接下来我们就可以调用browser来执行各种方法模拟浏览器的操作了。

    1.2. 访问页面

    进行页面访问使用的是get方法,传入参数为待访问页面的URL地址即可。

    from selenium import webdriver
    
    # 初始化浏览器为chrome浏览器
    browser = webdriver.Chrome()
    
    # 访问百度首页
    browser.get(r'https://www.baidu.com/')
    
    # 关闭浏览器
    browser.close()

    1.3. 设置浏览器大小

    set_window_size()方法可以用来设置浏览器大小(就是分辨率),而maximize_window则是设置浏览器为全屏!

    from selenium import webdriver
    import time  
    
    browser = webdriver.Chrome()
    
    # 设置浏览器大小:全屏
    browser.maximize_window()   
    browser.get(r'https://www.baidu.com')  
    time.sleep(2)
    
    # 设置分辨率 500*500
    browser.set_window_size(500,500)  
    time.sleep(2)
    
    # 设置分辨率 1000*800
    browser.set_window_size(1000,800) 
    time.sleep(2)
    
    # 关闭浏览器
    browser.close()

    这里就不截图了,大家自行演示看效果哈~

    1.4. 刷新页面

    刷新页面是我们在浏览器操作时很常用的操作,这里refresh()方法可以用来进行浏览器页面刷新。

    from selenium import webdriver
    import time  
    
    browser = webdriver.Chrome()
    
    # 设置浏览器全屏
    browser.maximize_window()   
    browser.get(r'https://www.baidu.com')  
    time.sleep(2)
    
    try:
        # 刷新页面
        browser.refresh()  
        print('刷新页面')
    except Exception as e:
        print('刷新失败')
       
    # 关闭浏览器
    browser.close()

    大家也是自行演示看效果哈,同F5快捷键。

    1.5. 前进后退

    前进后退也是我们在使用浏览器时非常常见的操作,这里forward()方法可以用来实现前进,back()可以用来实现后退。

    from selenium import webdriver
    import time  
    
    browser = webdriver.Chrome()
    
    # 设置浏览器全屏
    browser.maximize_window()   
    browser.get(r'https://www.baidu.com')  
    time.sleep(2)
    
    # 打开淘宝页面
    browser.get(r'https://www.taobao.com')  
    time.sleep(2)
    
    # 后退到百度页面
    browser.back()  
    time.sleep(2)
    
    # 前进的淘宝页面
    browser.forward() 
    time.sleep(2)
    
    # 关闭浏览器
    browser.close()

    2. 获取页面基础属性

    当我们用selenium打开某个页面,有一些基础属性如网页标题、网址、浏览器名称、页面源码等信息。

    from selenium import webdriver
    
    browser = webdriver.Chrome()
    browser.get(r'https://www.baidu.com') 
    
    # 网页标题
    print(browser.title)
    # 当前网址
    print(browser.current_url)
    # 浏览器名称
    print(browser.name)
    # 网页源码
    print(browser.page_source)

    输出如下:

    百度一下,你就知道
    https://www.baidu.com/
    chrome
    <html><head><script async="" src="https://passport.baidu.com/passApi/js/wrapper.js?cdnversion=1640515789507&amp;_=1640515789298"></script><meta http-equiv="Content-Type" content="text/html;charset=utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><meta content="always" name="referrer"><meta name="theme-color"..."
    

    需要注意的是,这里的页面源码我们就可以用正则表达式Bs4xpath以及pyquery等工具进行解析提取想要的信息了。

    3. 定位页面元素

    我们在实际使用浏览器的时候,很重要的操作有输入文本、点击确定等等。对此,Selenium提供了一系列的方法来方便我们实现以上操作。常说的8种定位页面元素的操作方式,我们一一演示一下!

    我们以百度首页的搜索框节点为例,搜索python

    搜索框的html结构:

    <input id="kw" name="wd" class="s_ipt" value="" maxlength="255" autocomplete="off">

    3.1. id定位

    find_element_by_id()根据id属性获取,这里id属性是 kw

    from selenium import webdriver
    import time  
    
    browser = webdriver.Chrome()
    
    browser.get(r'https://www.baidu.com')  
    time.sleep(2)
    
    # 在搜索框输入 python
    browser.find_element_by_id('kw').send_keys('python')
    time.sleep(2)
    
    # 关闭浏览器
    browser.close()

    3.2. name定位

    find_element_by_name()根据name属性获取,这里name属性是 wd

    from selenium import webdriver
    import time  
    
    browser = webdriver.Chrome()
    
    browser.get(r'https://www.baidu.com')  
    time.sleep(2)
    
    # 在搜索框输入 python
    browser.find_element_by_name('wd').send_keys('python')
    time.sleep(2)
    
    # 关闭浏览器
    browser.close()

    3.3. class定位

    find_element_by_class_name()根据class属性获取,这里class属性是s_ipt

    from selenium import webdriver
    import time  
    
    browser = webdriver.Chrome()
    
    browser.get(r'https://www.baidu.com')  
    time.sleep(2)
    
    # 在搜索框输入 python
    browser.find_element_by_class_name('s_ipt').send_keys('python')
    time.sleep(2)
    
    # 关闭浏览器
    browser.close()

    3.4. tag定位

    我们知道HTML是通过tag来定义功能的,比如input是输入,table是表格等等。每个元素其实就是一个tag,一个tag往往用来定义一类功能,我们查看百度首页的html代码,可以看到有很多同类tag,所以其实很难通过tag去区分不同的元素。

    find_element_by_tag_name()

    from selenium import webdriver
    import time  
    
    browser = webdriver.Chrome()
    
    browser.get(r'https://www.baidu.com')  
    time.sleep(2)
    
    # 在搜索框输入 python
    browser.find_element_by_tag_name('input').send_keys('python')
    time.sleep(2)
    
    # 关闭浏览器
    browser.close()

    由于存在多个input,以上代码会报错。

    3.5. link定位

    这种方法顾名思义就是用来定位文本链接的,比如百度首页上方的分类模块链接。

    find_element_by_link_text()

    以新闻为例

    from selenium import webdriver
    import time  
    
    browser = webdriver.Chrome()
    
    browser.get(r'https://www.baidu.com')  
    time.sleep(2)
    
    # 点击新闻 链接
    browser.find_element_by_link_text('新闻').click()
    time.sleep(2)
    
    # 关闭浏览器全部页面
    browser.quit()

    3.6. partial定位

    有时候一个超链接的文本很长,我们如果全部输入,既麻烦,又显得代码很不美观,这时候我们就可以只截取一部分字符串,用这种方法模糊匹配了。

    find_element_by_partial_link_text()

    from selenium import webdriver
    import time  
    
    browser = webdriver.Chrome()
    
    browser.get(r'https://www.baidu.com')  
    time.sleep(2)
    
    # 点击新闻 链接
    browser.find_element_by_partial_link_text('闻').click()
    time.sleep(2)
    
    # 关闭浏览器全部页面
    browser.quit()

    3.7. xpath定位

    前面介绍的几种定位方法都是在理想状态下,有一定使用范围的,那就是:在当前页面中,每个元素都有一个唯一idnameclass超链接文本的属性,那么我们就可以通过这个唯一的属性值来定位他们。

    但是在实际工作中并非有这么美好,那么这个时候我们就只能通过xpath或者css来定位了。

    find_element_by_xpath()

    from selenium import webdriver
    import time  
    
    browser = webdriver.Chrome()
    
    browser.get(r'https://www.baidu.com')  
    time.sleep(2)
    
    # 在搜索框输入 python
    browser.find_element_by_xpath("//*[@id='kw']").send_keys('python')
    time.sleep(2)
    
    # 关闭浏览器
    browser.close()

    3.8. css定位

    这种方法相对xpath要简洁些,定位速度也要快些。

    find_element_by_css_selector()

    from selenium import webdriver
    import time  
    
    browser = webdriver.Chrome()
    
    browser.get(r'https://www.baidu.com')  
    time.sleep(2)
    
    # 在搜索框输入 python
    browser.find_element_by_css_selector('#kw').send_keys('python')
    time.sleep(2)
    
    # 关闭浏览器
    browser.close()

    3.9. find_element的By定位

    除了上述的8种定位方法,Selenium还提供了一个通用的方法find_element(),这个方法有两个参数:定位方式和定位值。

    # 使用前先导入By类
    from selenium.webdriver.common.by import By

    以上的操作可以等同于以下:

    browser.find_element(By.ID,'kw')
    browser.find_element(By.NAME,'wd')
    browser.find_element(By.CLASS_NAME,'s_ipt')
    browser.find_element(By.TAG_NAME,'input')
    browser.find_element(By.LINK_TEXT,'新闻')
    browser.find_element(By.PARTIAL_LINK_TEXT,'闻')
    browser.find_element(By.XPATH,'//*[@id="kw"]')
    browser.find_element(By.CSS_SELECTOR,'#kw')

    3.10. 多个元素

    如果定位的目标元素在网页中不止一个,那么则需要用到find_elements,得到的结果会是列表形式。简单来说,就是element后面多了复数标识s,其他操作一致。

    4. 获取页面元素属性

    既然我们有很多方式来定位页面的元素,那么接下来就可以考虑获取以下元素的属性了,尤其是用Selenium进行网络爬虫的时候。

    4.1. get_attribute获取属性

    以百度首页的logo为例,获取logo相关属性

    <img hidefocus="true" id="s_lg_img" class="index-logo-src" src="//www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png" width="270" height="129" onerror="this.src='//www.baidu.com/img/flexible/logo/pc/index.png';this.onerror=null;" usemap="#mp">

    获取logo的图片地址

    from selenium import webdriver
    import time  
    
    browser = webdriver.Chrome()
    
    browser.get(r'https://www.baidu.com')  
    
    logo = browser.find_element_by_class_name('index-logo-src')
    print(logo)
    print(logo.get_attribute('src'))
    
    # 关闭浏览器
    browser.close()

    输出:

    <selenium.webdriver.remote.webelement.WebElement (session="e95b18c43a330745af019e0041f0a8a4", element="7dad5fc0-610b-45b6-b543-9e725ee6cc5d")>
    https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png

    4.2. 获取文本

    以热榜为例,获取热榜文本和链接

    <a class="title-content tag-width c-link c-font-medium c-line-clamp1" href="https://www.baidu.com/s?cl=3&amp;tn=baidutop10&amp;fr=top1000&amp;wd=%E5%90%84%E5%9C%B0%E8%B4%AF%E5%BD%BB%E5%8D%81%E4%B9%9D%E5%B1%8A%E5%85%AD%E4%B8%AD%E5%85%A8%E4%BC%9A%E7%B2%BE%E7%A5%9E%E7%BA%AA%E5%AE%9E&amp;rsv_idx=2&amp;rsv_dl=fyb_n_homepage&amp;sa=fyb_n_homepage&amp;hisfilter=1" target="_blank"><span class="title-content-index c-index-single c-index-single-hot1">1</span><span class="title-content-title">各地贯彻十九届六中全会精神纪实</span></a>

    获取热榜的文本,用的是text属性,直接调用即可

    from selenium import webdriver
    import time  
    
    browser = webdriver.Chrome()
    
    browser.get(r'https://www.baidu.com')  
    
    logo = browser.find_element_by_css_selector('#hotsearch-content-wrapper > li:nth-child(1) > a')
    print(logo.text)
    print(logo.get_attribute('href'))
    
    # 关闭浏览器
    browser.close()

    输出:

    1各地贯彻十九届六中全会精神纪实
    https://www.baidu.com/s?cl=3&tn=baidutop10&fr=top1000&wd=%E5%90%84%E5%9C%B0%E8%B4%AF%E5%BD%BB%E5%8D%81%E4%B9%9D%E5%B1%8A%E5%85%AD%E4%B8%AD%E5%85%A8%E4%BC%9A%E7%B2%BE%E7%A5%9E%E7%BA%AA%E5%AE%9E&rsv_idx=2&rsv_dl=fyb_n_homepage&sa=fyb_n_homepage&hisfilter=1

    4.3. 获取其他属性

    除了属性和文本值外,还有id、位置、标签名和大小等属性。

    from selenium import webdriver
    import time  
    
    browser = webdriver.Chrome()
    
    browser.get(r'https://www.baidu.com')  
    
    logo = browser.find_element_by_class_name('index-logo-src')
    print(logo.id)
    print(logo.location)
    print(logo.tag_name)
    print(logo.size)
    
    # 关闭浏览器
    browser.close()

    输出:

    6af39c9b-70e8-4033-8a74-7201ae09d540
    {'x': 490, 'y': 46}
    img
    {'height': 129, 'width': 270}

    5. 页面交互操作

    页面交互就是在浏览器的各种操作,比如上面演示过的输入文本、点击链接等等,还有像清除文本、回车确认、单选框与多选框选中等。

    5.1. 输入文本

    其实,在之前的小节中我们有用过此操作。

    send_keys()

    from selenium import webdriver
    import time  
    
    browser = webdriver.Chrome()
    browser.get(r'https://www.baidu.com')  
    time.sleep(2)
    
    # 定位搜索框
    input = browser.find_element_by_class_name('s_ipt')
    # 输入python
    input.send_keys('python')
    time.sleep(2)
    
    # 关闭浏览器
    browser.close()

    5.2. 点击

    同样,我们也用过这个点击操作。

    click()

    from selenium import webdriver
    import time  
    
    browser = webdriver.Chrome()
    browser.get(r'https://www.baidu.com')  
    time.sleep(2)
    
    # 选中新闻按钮
    click = browser.find_element_by_link_text('新闻')
    # 点击之
    click.click()
    time.sleep(2)
    
    # 关闭浏览器全部页面
    browser.quit()

    5.3. 清除文本

    既然有输入,这里也就有清除文本啦。

    clear()

    from selenium import webdriver
    import time  
    
    browser = webdriver.Chrome()
    browser.get(r'https://www.baidu.com')  
    time.sleep(2)
    
    # 定位搜索框
    input = browser.find_element_by_class_name('s_ipt')
    # 输入python
    input.send_keys('python')
    time.sleep(2)
    # 清除python
    input.clear()
    time.sleep(2)
    
    # 关闭浏览器
    browser.close()

    5.4. 回车确认

    比如,在搜索框输入文本python,然后回车就出查询操作结果的情况。

    submit()

    from selenium import webdriver
    import time  
    
    browser = webdriver.Chrome()
    browser.get(r'https://www.baidu.com')  
    time.sleep(2)
    
    # 定位搜索框
    input = browser.find_element_by_class_name('s_ipt')
    # 输入python
    input.send_keys('python')
    time.sleep(2)
    # 回车查询
    input.submit()
    time.sleep(5)
    
    # 关闭浏览器
    browser.close()

    5.5. 单选

    单选比较好操作,先定位需要单选的某个元素,然后点击一下即可。

    5.6. 多选

    多选好像也比较容易,依次定位需要选择的元素,点击即可。

    5.7. 下拉框

    下拉框的操作相对复杂一些,需要用到Select模块。

    先导入该类

    from selenium.webdriver.support.select import Select

    select模块中有以下定位方法

    '''1、三种选择某一选项项的方法'''
    
    select_by_index()           # 通过索引定位;注意:index索引是从“0”开始。
    select_by_value()           # 通过value值定位,value标签的属性值。
    select_by_visible_text()    # 通过文本值定位,即显示在下拉框的值。
    
    '''2、三种返回options信息的方法'''
    
    options                     # 返回select元素所有的options
    all_selected_options        # 返回select元素中所有已选中的选项
    first_selected_options      # 返回select元素中选中的第一个选项                  
    
    
    '''3、四种取消选中项的方法'''
    
    deselect_all                # 取消全部的已选择项
    deselect_by_index           # 取消已选中的索引项
    deselect_by_value           # 取消已选中的value值
    deselect_by_visible_text    # 取消已选中的文本值

    我们来进行演示一波,由于暂时没找到合适的网页,我这边写了一个简单的网页本地测试(文件存为 帅哥.html)

    <html>
    
    <body>
    
    <form>
    <select name="帅哥">
    <option value="才哥">才哥</option>
    <option value="小明" selected="">小明</option>
    <option value="小华">小华</option>
    <option value="草儿">小草</option>
    </select>
    </form>
    
    </body>
    </html>

    然后,再演示下拉框的不同选择的方式

    from selenium import webdriver
    from selenium.webdriver.support.select import Select
    import time
    
    url = 'file:///C:/Users/Gdc/Desktop/帅哥.html'
    
    browser = webdriver.Chrome()
    
    browser.get(url)
    time.sleep(2)
    
    # 根据索引选择
    Select(browser.find_element_by_name("帅哥")).select_by_index("2")
    time.sleep(2)
    # 根据value值选择
    Select(browser.find_element_by_name("帅哥")).select_by_value("草儿")
    time.sleep(2)
    # 根据文本值选择
    Select(browser.find_element_by_name("帅哥")).select_by_visible_text("才哥")
    time.sleep(2)
    
    # 关闭浏览器
    browser.close()
    动图封面

    6. 多窗口切换

    比如同一个页面的不同子页面的节点元素获取操作,不同选项卡之间的切换以及不同浏览器窗口之间的切换操作等等。

    6.1. Frame切换

    Selenium打开一个页面之后,默认是在父页面进行操作,此时如果这个页面还有子页面,想要获取子页面的节点元素信息则需要切换到子页面进行擦走,这时候switch_to.frame()就来了。如果想回到父页面,用switch_to.parent_frame()即可。

    6.2. 选项卡切换

    我们在访问网页的时候会打开很多个页面,在Selenium中提供了一些方法方便我们对这些页面进行操作。

    current_window_handle:获取当前窗口的句柄。
    window_handles:返回当前浏览器的所有窗口的句柄。
    switch_to_window():用于切换到对应的窗口。

    from selenium import webdriver
    import time
    
    browser = webdriver.Chrome()
    
    # 打开百度
    browser.get('http://www.baidu.com')
    # 新建一个选项卡
    browser.execute_script('window.open()')
    print(browser.window_handles)
    # 跳转到第二个选项卡并打开知乎
    browser.switch_to.window(browser.window_handles[1])
    browser.get('http://www.zhihu.com')
    # 回到第一个选项卡并打开淘宝(原来的百度页面改为了淘宝)
    time.sleep(2)
    browser.switch_to.window(browser.window_handles[0])
    browser.get('http://www.taobao.com')

    7. 模拟鼠标操作

    既然是模拟浏览器操作,自然也就需要能模拟鼠标的一些操作了,这里需要导入ActionChains 类。

    from selenium.webdriver.common.action_chains import ActionChains

    7.1. 左键

    这个其实就是页面交互操作中的点击click()操作。

    7.2. 右键

    context_click()

    from selenium.webdriver.common.action_chains import ActionChains
    from selenium import webdriver
    import time  
    
    browser = webdriver.Chrome()
    browser.get(r'https://www.baidu.com')  
    time.sleep(2)
    
    # 定位到要右击的元素,这里选的新闻链接
    right_click = browser.find_element_by_link_text('新闻')
    
    # 执行鼠标右键操作
    ActionChains(browser).context_click(right_click).perform()
    time.sleep(2)
    
    # 关闭浏览器
    browser.close()

    在上述操作中

    ActionChains(browser):调用ActionChains()类,并将浏览器驱动browser作为参数传入
    context_click(right_click):模拟鼠标双击,需要传入指定元素定位作为参数
    perform():执行ActionChains()中储存的所有操作,可以看做是执行之前一系列的操作

    7.3. 双击

    double_click()

    from selenium.webdriver.common.action_chains import ActionChains
    from selenium import webdriver
    import time  
    
    browser = webdriver.Chrome()
    browser.get(r'https://www.baidu.com')  
    time.sleep(2)
    
    # 定位到要双击的元素
    double_click = browser.find_element_by_css_selector('#bottom_layer > div > p:nth-child(8) > span')
    
    # 双击
    ActionChains(browser).double_click(double_click).perform()
    time.sleep(15)
    
    # 关闭浏览器
    browser.close()

    7.4. 拖拽

    drag_and_drop(source,target)拖拽操作嘛,开始位置和结束位置需要被指定,这个常用于滑块类验证码的操作之类。

    我们以菜鸟教程的一个案例来进行演示

    https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable
    from selenium.webdriver.common.action_chains import ActionChains
    from selenium import webdriver
    import time  
    
    browser = webdriver.Chrome()
    url = 'https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable'
    browser.get(url)  
    time.sleep(2)
    
    browser.switch_to.frame('iframeResult')
    
    # 开始位置
    source = browser.find_element_by_css_selector("#draggable")
    
    # 结束位置
    target = browser.find_element_by_css_selector("#droppable")
    
    # 执行元素的拖放操作
    actions = ActionChains(browser)
    actions.drag_and_drop(source, target)
    actions.perform()
    # 拖拽
    time.sleep(15)
    
    # 关闭浏览器
    browser.close()
    动图封面

    7.5. 悬停

    move_to_element()

    from selenium.webdriver.common.action_chains import ActionChains
    from selenium import webdriver
    import time  
    
    browser = webdriver.Chrome()
    url = 'https://www.baidu.com'
    browser.get(url)  
    time.sleep(2)
    
    # 定位悬停的位置
    move = browser.find_element_by_css_selector("#form > span.bg.s_ipt_wr.new-pmd.quickdelete-wrap > span.soutu-btn")
    
    # 悬停操作
    ActionChains(browser).move_to_element(move).perform()
    time.sleep(5)
    
    # 关闭浏览器
    browser.close()

    8. 模拟键盘操作

    selenium中的Keys()类提供了大部分的键盘操作方法,通过send_keys()方法来模拟键盘上的按键。

    引入Keys

    from selenium.webdriver.common.keys import Keys

    常见的键盘操作

    send_keys(Keys.BACK_SPACE):删除键(BackSpace)
    send_keys(Keys.SPACE):空格键(Space)
    send_keys(Keys.TAB):制表键(TAB)
    send_keys(Keys.ESCAPE):回退键(ESCAPE)
    send_keys(Keys.ENTER):回车键(ENTER)
    send_keys(Keys.CONTRL,'a'):全选(Ctrl+A)
    send_keys(Keys.CONTRL,'c'):复制(Ctrl+C)
    send_keys(Keys.CONTRL,'x'):剪切(Ctrl+X)
    send_keys(Keys.CONTRL,'v'):粘贴(Ctrl+V)
    send_keys(Keys.F1):键盘F1
    …..
    send_keys(Keys.F12):键盘F12

    实例操作演示:

    定位需要操作的元素,然后操作即可!

    from selenium.webdriver.common.keys import Keys
    from selenium import webdriver
    import time
    
    browser = webdriver.Chrome()
    url = 'https://www.baidu.com'
    browser.get(url)  
    time.sleep(2)
    
    # 定位搜索框
    input = browser.find_element_by_class_name('s_ipt')
    # 输入python
    input.send_keys('python')
    time.sleep(2)
    
    # 回车
    input.send_keys(Keys.ENTER)
    time.sleep(5)
    
    # 关闭浏览器
    browser.close()

    9. 延时等待

    如果遇到使用ajax加载的网页,页面元素可能不是同时加载出来的,这个时候尝试在get方法执行完成时获取网页源代码可能并非浏览器完全加载完成的页面。所以,这种情况下需要设置延时等待一定时间,确保全部节点都加载出来。

    三种方式可以来玩玩:强制等待、隐式等待和显式等待

    9.1. 强制等待

    就很简单了,直接time.sleep(n)强制等待n秒,在执行get方法之后执行。

    9.2. 隐式等待

    implicitly_wait()设置等待时间,如果到时间有元素节点没有加载出来,就会抛出异常。

    from selenium import webdriver
    
    browser = webdriver.Chrome()
    
    # 隐式等待,等待时间10秒
    browser.implicitly_wait(10)  
    
    browser.get('https://www.baidu.com')
    print(browser.current_url)
    print(browser.title)
    
    # 关闭浏览器
    browser.close()

    9.3. 显式等待

    设置一个等待时间和一个条件,在规定时间内,每隔一段时间查看下条件是否成立,如果成立那么程序就继续执行,否则就抛出一个超时异常。

    from selenium import webdriver
    from selenium.webdriver.support.wait import WebDriverWait
    from selenium.webdriver.support import expected_conditions as EC
    from selenium.webdriver.common.by import By
    import time
    
    browser = webdriver.Chrome()
    browser.get('https://www.baidu.com')
    # 设置等待时间10s
    wait = WebDriverWait(browser, 10)
    # 设置判断条件:等待id='kw'的元素加载完成
    input = wait.until(EC.presence_of_element_located((By.ID, 'kw')))
    # 在关键词输入:关键词
    input.send_keys('Python')
    
    # 关闭浏览器
    time.sleep(2)
    browser.close()

    WebDriverWait的参数说明

    WebDriverWait(driver,timeout,poll_frequency=0.5,ignored_exceptions=None)
    driver: 浏览器驱动
    timeout: 超时时间,等待的最长时间(同时要考虑隐性等待时间)
    poll_frequency: 每次检测的间隔时间,默认是0.5秒
    ignored_exceptions:超时后的异常信息,默认情况下抛出NoSuchElementException异常
    until(method,message=”)
    method: 在等待期间,每隔一段时间调用这个传入的方法,直到返回值不是False
    message: 如果超时,抛出TimeoutException,将message传入异常
    until_not(method,message=”)
    until_not 与until相反,until是当某元素出现或什么条件成立则继续执行,until_not是当某元素消失或什么条件不成立则继续执行,参数也相同。

    其他等待条件

    from selenium.webdriver.support import expected_conditions as EC
    
    # 判断标题是否和预期的一致
    title_is
    # 判断标题中是否包含预期的字符串
    title_contains
    
    # 判断指定元素是否加载出来
    presence_of_element_located
    # 判断所有元素是否加载完成
    presence_of_all_elements_located
    
    # 判断某个元素是否可见. 可见代表元素非隐藏,并且元素的宽和高都不等于0,传入参数是元组类型的locator
    visibility_of_element_located
    # 判断元素是否可见,传入参数是定位后的元素WebElement
    visibility_of
    # 判断某个元素是否不可见,或是否不存在于DOM树
    invisibility_of_element_located
    
    # 判断元素的 text 是否包含预期字符串
    text_to_be_present_in_element
    # 判断元素的 value 是否包含预期字符串
    text_to_be_present_in_element_value
    
    #判断frame是否可切入,可传入locator元组或者直接传入定位方式:id、name、index或WebElement
    frame_to_be_available_and_switch_to_it
    
    #判断是否有alert出现
    alert_is_present
    
    #判断元素是否可点击
    element_to_be_clickable
    
    # 判断元素是否被选中,一般用在下拉列表,传入WebElement对象
    element_to_be_selected
    # 判断元素是否被选中
    element_located_to_be_selected
    # 判断元素的选中状态是否和预期一致,传入参数:定位后的元素,相等返回True,否则返回False
    element_selection_state_to_be
    # 判断元素的选中状态是否和预期一致,传入参数:元素的定位,相等返回True,否则返回False
    element_located_selection_state_to_be
    
    #判断一个元素是否仍在DOM中,传入WebElement对象,可以判断页面是否刷新了
    staleness_of

    10. 其他

    补充一些

    10.1. 运行JavaScript

    还有一些操作,比如下拉进度条,模拟javaScript,使用execute_script方法来实现。

    from selenium import webdriver
    
    browser = webdriver.Chrome()
    # 知乎发现页
    browser.get('https://www.zhihu.com/explore')
    
    browser.execute_script('window.scrollTo(0, document.body.scrollHeight)')
    browser.execute_script('alert("To Bottom")')

    10.2. Cookie

    selenium使用过程中,还可以很方便对Cookie进行获取、添加与删除等操作。

    from selenium import webdriver
    
    browser = webdriver.Chrome()
    # 知乎发现页
    browser.get('https://www.zhihu.com/explore')
    # 获取cookie
    print(f'Cookies的值:{browser.get_cookies()}')
    # 添加cookie
    browser.add_cookie({'name':'才哥', 'value':'帅哥'})
    print(f'添加后Cookies的值:{browser.get_cookies()}')
    # 删除cookie
    browser.delete_all_cookies()
    print(f'删除后Cookies的值:{browser.get_cookies()}')

    输出:

    Cookies的值:[{'domain': '.zhihu.com', 'httpOnly': False, 'name': 'Hm_lpvt_98beee57fd2ef70ccdd5ca52b9740c49', 'path': '/', 'secure': False, 'value': '1640537860'}, {'domain': '.zhihu.com', ...]
    添加后Cookies的值:[{'domain': 'www.zhihu.com', 'httpOnly': False, 'name': '才哥', 'path': '/', 'secure': True, 'value': '帅哥'}, {'domain': '.zhihu.com', 'httpOnly': False, 'name': 'Hm_lpvt_98beee57fd2ef70ccdd5ca52b9740c49', 'path': '/', 'secure': False, 'value': '1640537860'}, {'domain': '.zhihu.com',...]
    删除后Cookies的值:[]

    以上就是本次有关Selenium的全部内容,后续我们将演示Selenium爬虫以及web自动化方面的一些实战案例,敬请期待!

  • 漫城CMS(mccms)开源全源码

    漫城CMS(mccms)开源全源码

    漫城CMS程序是一套采用CI框架内核进行开发,在PHP+MYSQL环境下运行的完善而强大的快速建站系统。经过近多年的开发经验和技术积累,漫城CMS程序即将成为全网最强漫画+小说系统,在易用性和功能上已经成为同行中的佼佼者。MVC模板分离,内置标签,自定义函数标签接口,前后端分离全部100%开源。为开发者赋能,助力企业发展、国家富强,致力于打造最受欢迎的数字阅读项目

    运行环境

    Apache/Nginx/IIS
    PHP 5.3 ~ 7.4
    MySQL 5.5 ~ 5.8
    

    使用协议

    请您在使用(漫城CMS)前仔细阅读如下条款。包括免除或者限制作者责任的免责条款及对用户的权利限制。您的安装使用行为将视为对本《用户许可协议》的接受,并同意接受本《用户许可协议》各项条款的约束。
    1、安装和使用:
    (漫城CMS授权提供给您使用的,您可安装无限制数量副本。 您必须保证在不进行非法活动,不违反所在国家相关政策法规的前提下使用本软件。
    2、协议规定的约束和限制:
    禁止在(漫城CMS)整体或任何部分基础上发展任何派生版本、修改版本或第三方版本用于重新分发销售。

    免责声明

    漫城CMS是一款开源漫画小说cms系统。程序的著作权均归作者所有,用户具有自由的使用权。
    1、您承诺秉着合法、合理的原则使用 漫城CMS系统,不利用 漫城CMS系统 进行任何违法、侵害他人合法利益等恶意的行为,亦不将 漫城CMS系统 运用于任何违反我国法律法规的 Web 平台。
    2、任何单位或个人因下载使用 漫城CMS系统 而产生的任何意外、疏忽、合约毁坏、诽谤、版权或知识产权侵犯及其造成的损失 (包括但不限于直接、间接、附带或衍生的损失等),本团队不承担任何法律责任。
    3、用户明确并同意本声明条款列举的全部内容,对使用 漫城CMS系统 组件库可能存在的风险和相关后果将完全由用户自行承担,本团队不承担任何法律责任。

  • PTCMS 4.0 开源源码

    PTCMS 4.0 开源源码

    由于本站系统升级,此源码已迁移到以下页面:

    [b2_insert_post id=”3700″]

    无加密解密版源码,对ptcms优化,支持PHP7.2、MySQL5.7

    非4.2.8或4.3.0版本,但功能差不多。

  • YGBOOK开源版无加密源码


    源码介绍

    YGBOOK全自动采集小说系统 源码已经完美修复各类BUG,所有文件都已经解密完成,深度SEO源码,批量全自动后台采集,并且不需要很大的硬盘即可安装,易云采集了20W本小说占用不到20G,自带4条采集规则,源码压缩包有安装教程,跟着安装很简单的

    源码截图

    YGBOOK全自动采集小说系统源码-酷库博客-第4张图片
  • 杰奇2.4开源版无加密源码

    • Jieqi2.4 解密开源程序

    1、服务器环境
    php5.3.– php7.1 + mysql 5. – MariaDB 10.*

    php请加载以下模块
    mysql zlib sockets curl iconv mbstring gd

    2、更改网站默认编码为uft8,采集GBK自动转为utf8,创建数据库编码为utf8mb4,把sql目录下的jieqi2.4.sql导入数据库,作为网站初始的数据库结构及数据,更改默认存储为INNODB

    3、上传网站程序后,以下4个目录必须可写:cache compiled configs files

    4、编辑网站目录下 /configs/define.php ,以下数据库参数根据实际填写
    @define(‘JIEQI_DB_HOST’,’localhost’); //数据库服务器地址,跟网站在同一服务器时候填localhost
    @define(‘JIEQI_DB_USER’,’root’); //数据库登录账号
    @define(‘JIEQI_DB_PASS’,’pass’); //数据库登录密码
    @define(‘JIEQI_DB_NAME’,’jieqicms’); //网站系统使用的数据库名字

    5、默认管理员账号密码:
    admin
    jieqi.com

    6、有彩蛋,能支持到PHP7.3

    网站后台 http://www.***.com/admin/
    进入后台后可具体设置权限、参数等

    正式使用时,请在前台会员中心修改默认的管理员密码

    小说分类修改不在后台,请直接编辑 /configs/article/sort.php

    6、
    网站模板修改规范请参考 http://help.jieqi.com/template/index.html
    登录充值接口申请,请参考“登录充值接口.txt”
    网站授权设置请参考“软件授权.txt”

    官方网站及联系方式请访问: http://www.jieqi.com

    电脑版和手机版网站同时安装配置方法:

    1、电脑版和手机版网站使用两个独立目录,但是共用数据库和数据文件。默认www为电脑版程序目录,建议绑定域名 www..com;m为手机版程序目录,建议绑定域名m..com
    2、编辑手机版网站目录下的 /configs/define.php ,数据库连接设置跟电脑版保持一致
    3、如果修改过分类文件 /configs/article/sort.php,请手机版和电脑版保持一致
    4、默认程序生成的文件件保存在电脑版网站的 files 目录下,手机版网站也需要读写同一个目录。linux下建议用ls命令建立一个链接把手机站的files目录指向电脑站的files。
    windows可以考虑电脑和手机版里面都指定存储目录的绝对路径和访问url,这两个参数在后台 系统管理-系统定义 里面的“数据文件保存路径”和“访问数据文件的URL”。(比如“数据文件保存路径”设置成 E:/web/www/files,“访问数据文件的URL”设置成 http://www.jieqi.com/files)

  • 小浣熊小说CMS源码

    开源有态度的小说CMS,永久免费

    程序特色

    • 兼容杰奇数据库,可以使用关关采集
    • 独创的泛目录站群模式,秒杀一些陈旧的站群系统
    • 完善的书架、阅读历史功能
    • 有功能强大的采集,可以运行于Linux
    • 章节预加载,在阅读下一章时不需要再等待载入

    开源协议

    本程序采用MIT协议开源

    截图

    免责声明

    小浣熊小说cms是一款不以盈利为目的的开源小说cms系统。程序的著作权均归作者所有,用户具有自由的使用权。 如果用户下载、安装、使用本系统,即表明用户信任该系统。那么,用户在使用本系统时造成对用户自己或他人任何形式的损失和伤害,作者不承担任何责任。 本系统只提供做小说系统最基本的功能和程序,未提供任何可以让使用者违法使用、牟利(如侵权盗版、涉黄、非法采集他人数据等)的功能。用户使用本系统从事任何违法违规的事情,一切后果由用户自行承担,作者不承担任何责任。

    许可声明

    下载、安装和使用:本系统永久免费,不会盈利,用户可以无限制次数下载、安装本系统。 复制、分发和传播:用户可以无限制次数复制、分发和传播本系统。但必须保证复制、分发和传播的程序的完整性和真实性,需包括所有有关本系统的软件、电子文档, 版权和商标及本协议等。

    使用声明

    本系统不含有任何旨在破坏用户计算机数据和获取用户隐私信息的恶意代码;不含有任何跟踪、监视用户计算机功能的代码;不含有监控用户网上、网下行为的功能;不含有收集用户的其它软件、文档中包含的个人信息的功能;不会泄漏用户隐私。 本系统唯一官方下载途径就是GitHub,对于用户从官方途径下载的系统以及从非作者发行的介质上获得的系统,作者无法保证其是否感染计算机病毒、是否隐藏有伪装的特洛伊木马程序或者黑客软件。用户使用此类软件,将可能导致不可预测的风险,建议用户不要轻易下载、安装、使用。作者不承担由此产生的一切法律责任。 用户不得利用本系统误导、欺骗他人;不得故意避开或者破坏作者为保护本系统著作权而采取的技术措施。

  • 开源有态度的漫画CMS 小浣熊源码

    因为原作者跑路了,我看有部分朋友也有需要就上传了 部分文档在doc目录下,可自行查看

    此版是小浣熊系统的最终版本

    小浣熊的收费功能比较完善,适合做漫画盈利站。

    程序特色

    • 完善的会员系统,带支付系统,带VIP功能,带推广功能
    • 完善的APP接口
    • 完善的火车头API
    • 阅读页可以选择一页几图,为网站增加PV
    • 章节预加载,在阅读下一章时不需要再等待载入
    • 三端并存:PC端,移动端,MIP端,MIP端可以跳转到移动端的阅读页,解决MIP不能放广告的问题(MIP做引流,移动端做真正变现)
    • 作者系统
    • 对接公众号系统
    • 前台一些列表页面使用vue上拉加载,用户体验更好

  • 基于小浣熊漫画小说聚合采集工具

    1.概述

    聚合采集工具是基于python中scrapy+redis+mysql分布式采集工具的基础上进行开发的一套采集工具.另寄托于docker基础上运行,所以天然支持异步和多机采集工具另外特别方便部署,它采集时会自动入库到mysql数据库,并通过入库的数据进行向远端火车头api发起入库请求.这些操作每天都会定时全自动执行.

    2.使用环境支持

    1、windows系列,winserver和windows8及以上
    2、linux内核系统比如 centos7,ubuntu,macos等系统支持
    3、简单讲就是支持docker的系统那就支持

    2.安装教程 linux安装

    聚合采集工具安装流程我单独增加了linux sh脚本一键式安装,安装过程分为三步

    1.安装docker,如果已安装则跳过

     sh docker-install.sh
    

    2.安装docker-compose ,如果已安装则跳过

    sh docker-compose-install.sh
    

    3.自动下载所需文件并启动

    cd /opt && tar -zxvf spider.tar.gz && cd /opt/spider && docker-compose up -d
    

    3.爬虫管理命令合集

    # 爬虫启动推荐使用命令 先执行更新爬虫,再开启爬虫
    docker pull jhspider/spider:test && cd /opt/spider && docker-compose up -d
    
    # 爬虫重启推荐使用命令 先执行更新爬虫,再停止爬虫,最后再开启爬虫
    docker pull jhspider/spider:test && cd /opt/spider && docker-compose down && docker-compose up -d
    
    # 更新爬虫
    docker pull jhspider/spider:test
    # 启动爬虫
    docker-compose up -d
    # 关闭爬虫
    docker-compose down
    # 重启爬虫
    docker-compose restart
    # 查看爬虫日志,刚开始时常用该命令去看配置情况
    docker logs jh-spider --tail 1000
    

    安装完成后

    ![](/upload/editormd/20221020/6350cc9d4e397.png)

    大概效果图如上
    这个时候你可以输入docker-compose ps指令查看运行状况

    最后要配置发布

    自行连接到数据库,开放33061端口到外网后通过工具去访问 连接信息请往下看,暂时只支持漫城跟小浣熊平台的发布 发布配置是在 spider_db数据库中的publish_site数据表里去做配置 具体配置参数请参考下面的表格

    暂时只支持漫画源 主要用与发布时筛选漫画源

    qinqinmh 
    twhm
    qiman
    qimiaomh
    dmzj 动漫之家
    sixmh
    xianman
    tx550
    kuman5
    
    switchhostparamtypewherename
    字段参考值1http://www.xxxxx.com{“api_key”:”hahmh”}0qiman,tx550,dmzj,kuman5测试发布站点
    字段描述0=关 1=开发布host地址如果没做伪静态要加上http://www.xxxx.com/index.phpapi_key里面填写发布密钥类型 0=小浣熊 1=漫城漫画 2=漫城小说指定发布采集源为空则全部发布,这边添加英文漫画源名比如 qinqinmh 如果多项则逗号隔开这个应该都懂的

    如果没有漫画站可以先用我的做测试,直接复制sql命令并插入即可

    INSERT INTO `spider_db`.`publish_site` (`id`, `switch`, `host`, `param`, `type`, `thread_num`, `where`, `name`, `create_time`, `update_time`) VALUES (7, 1, 'http://www.52hah.com', '{\"api_key\":\"hahmh\"}', 0, 2, 'qiman,dmzj', '测试', '2022-11-01 13:42:44', '2022-11-01 13:42:44');
    

    附录 默认mysql和redis账号密码

    mysql-主机: 127.0.0.1或局域网ip也可外网ip
    mysql-用户名: root
    mysql-密码: jhspider_pass
    mysql-数据库 : spider-db
    mysql-端口:33061
    
    redis-主机: 127.0.0.1或局域网ip也可外网ip
    redis-用户名: root
    redis-密码: jhspider_pass
    redis-端口:63791
    部署后可自行更改
    

    结尾

    采集工具的话初次使用都会自动赠送3天测试时间,如果出现问题或者想要继续使用可以联系我免费增加使用时间也可以增加漫画采集源初次使用只赠送1-2个漫画源采集

    默认都是盗链的,当然我这也提供了python高性能图片本地化方案 需要也可以联系我咨询,彻底解决小白建漫画站的所有问题

    最终效果图

    如果安装时报wget未安装则可以按照以下步骤进行

    mac系统 brew install wget
    centos系统 yum install wget
    ubuntu apt-get install wget
  • 杰奇cms官方版实现笔趣阁手机端分页样式

    最近在搞杰奇cms仿制一个类似笔趣阁的模板,看到它的手机端的时候,其实就知道这个分页肯定是官方版直接调用无法实现的,但是其实只要思路明确的话,利用js可以很好的实现整个效果。原理就是利用jquery根据class获取到分页的那些参数,然后一个for循环直接加进dom层。还算完美,以下是效果图(手机浏览器进入):

    这个界面是完全抓取的现成的模板,所以css样式也是现成的,就不需要自己再去写了,所以如果以后要把17mb那些的手机端,移植到杰奇的官方程序上,这篇教程是很适用的。

    代码如下:

    总共分为三个部分,这是全部的代码,不过记得要先引入jquery

    <div class="listpage"><span class="left"><a class="before" href="javascript:;">上一页</a></span><span class="middle"><select name="pageselect" id="pageselect" onchange="self.location.href=options[selectedIndex].value">
        </select></span><span class="right"><a href="javascript:;" class="onclick">下一页</a></span></div>
        
        <div id="pageTo" style="display:none;">{?$url_jumppage?}</div>
        <script>
            $(function(){
                var first = parseInt($("#pagelink .first").text());
                var last = parseInt($("#pagelink .last").text());
                var page = parseInt($("#pagelink strong").text());
                var next;
                var prev;
                if(page > 1){
                    var prev = $("#pagelink .prev").attr("href");
                    $(".listpage .before").attr("href",prev);
                }
                if(page < last){
                    var next = $("#pagelink .next").attr("href");
                    $(".listpage .onclick").attr("href",next);
                }
                var op = "";
                for(var i = 1; i < last; i++){
                    var k = (i-1)*20+1; 
                    var j = i*20;
                    if(i==page){
                        op += "<option value=\"/book/{?$articleid?}_"+i+"/\" selected=\"selected\">第"+k+" - "+j+"章</option>";
                    }else{
                        op += "<option value=\"/book/{?$articleid?}_"+i+"/\">第"+k+" - "+j+"章</option>";
                    }
                    
                }
                $("#pageselect").html(op);
                //alert(op);
            });
        </script>
    

    1.第一部分是改造原本的分页代码,比如原本的是这样,option标签一路循环下去,上一页下一页也是通过php调用出来的,但是杰奇根本做不到的。

    所以,原本的代码就完全精简为如下了:

    <div class="listpage"><span class="left"><a class="before" href="javascript:;">上一页</a></span><span class="middle"><select name="pageselect" id="pageselect" onchange="self.location.href=options[selectedIndex].value">
        </select></span><span class="right"><a href="javascript:;" class="onclick">下一页</a></span></div>
    

    这样的意思是,无论是上一页,下一页,还是中间的下拉选项全部都留空了,不给任何值,因为所有的值将交给js处理,让js将杰奇原本的分页代码转进去。

    2.放置杰奇的原本分页代码,但是默认隐藏起来。

    <div id="pageTo" style="display:none;">{?$url_jumppage?}</div>
    

    这句代码,在实际网页访问中,会输出所有的分页和上一页下一页那些,但是不是按照模板需要格式来的,而且二开也特别麻烦。所以默认隐藏了,不过如果看源代码视图,就会发现它的关键值都是有class的,所以完全能让js来读取。

    3.利用js读取原本的分页代码关键词,然后生成html代码,渲染进分页的代码中。

        <script>
            $(function(){
                var first = parseInt($("#pagelink .first").text());
                var last = parseInt($("#pagelink .last").text());
                var page = parseInt($("#pagelink strong").text());
                var next;
                var prev;
                if(page > 1){
                    var prev = $("#pagelink .prev").attr("href");
                    $(".listpage .before").attr("href",prev);
                }
                if(page < last){
                    var next = $("#pagelink .next").attr("href");
                    $(".listpage .onclick").attr("href",next);
                }
                var op = "";
                for(var i = 1; i < last; i++){
                    var k = (i-1)*20+1; 
                    var j = i*20;
                    if(i==page){
                        op += "<option value=\"/book/{?$articleid?}_"+i+"/\" selected=\"selected\">第"+k+" - "+j+"章</option>";
                    }else{
                        op += "<option value=\"/book/{?$articleid?}_"+i+"/\">第"+k+" - "+j+"章</option>";
                    }
                    
                }
                $("#pageselect").html(op);
                //alert(op);
            });
        </script>
    

    读取了第一页的数字,最后一页的数字,当前页的数字,并且知道每页输出20个,于是就可以把这些值处理后赋值到分页代码中,而被删除的option标签对,也就由js生成,一起放进select中了。有i和j这两个字段,就是显示多少章到多少章,简单的计算公式就可以实现。

    是不是思路一下子就清晰了。