解决python爬虫AttributeError: ‘NoneType‘ object has no attribute ‘find_all‘

本文讲述了在Python爬虫过程中遇到的网页异地加密数据问题。通过分析网页源码、理解加密数据来源于JS文件,以及使用无头浏览器模拟加载解决加密问题。最终,通过无头浏览器动态解析获取完整HTML,成功抓取所需数据。
摘要由CSDN通过智能技术生成

网页上异地加密数据的python爬虫

这里特别感谢钟思哲大佬帮我找到了问题并帮助解决

要搞python大作业
想从https://www.endata.com.cn/BoxOffice/BO/Year/index.html爬一个年度票房的数据出来
我参考了bilibili上的视频https://www.bilibili.com/video/BV1SJ411s7w5?
上面说的在网页上右击选择检查
在这里插入图片描述

之后右侧出现这个网页源码调试界面

在这里插入图片描述
对照着应该是从这个table中获取数据对吧
但是在这个地方遇到了问题

import requests
from bs4 import BeautifulSoup

proxies = {
   
    "http" : "http://108.108.108.108:8899" # 代理ip
}
user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
headers = {
    'User-Agent' : user_agent }
#这个是一开始怀疑这个网站有反爬虫的设置 看了csdn上几篇关于应对反爬虫策略的文章 说是要模拟浏览器访问的过程 所以多设置了这些属性 
#但是对这个project来说问题不在这里

text= requests.get(url="https://www.endata.com.cn/BoxOffice/BO/Year/index.html",).text
main_page= BeautifulSoup(text,"html.parser")
table = main_page.find("table",attrs={
   "class":"bo-table img-table"})

print(table)
# 找的是一堆标签
f= open("电影票房.csv",mode="a")

div=table.find_all("div",attrs={
   "class":"hb"})
ps=div.find_all("p")
for p in ps:
    print(p.text)


python爬出来说<table里面是空的
我一开始怀疑这个网站有反爬虫的设置
看了csdn上几篇关于应对反爬虫策略的文章,说是要模拟浏览器访问的过程,所以在requests的get()里面多设置了这些属性
但是对这个project来说问题不在这里

首先我之前点的那个检查相当于是页面已经加载出来了之后的代码,但是并不是网页的源代码
网页源代码是这个样子的
在这里插入图片描述

可以看到这里面并没有上图的那个table和里面的tr什么的

<script>
function SetOption_Date(){
   
for(var t="",a=(new Date).getFullYear();2008<=a;a--)t+='<option value="'+a+'">'+a+"年</option>";$("#OptionDate").html(t).on("change",function(){
   GetData_TableList()}).change()}
function GetData_TableList({
   
$Common.Popup.LoadingShow("WrapInfo"),$Common.Ajax.PostAPI("BoxOffice_GetYearInfoData","year="+$("#OptionDate option:selected").val(),function(t){
   $Common.Popup.LoadingHide("WrapInfo"),t&&t.Table&&0<t.Table.length?$Common.Data.InitTemplate("TP_TableList",t.Table,function(t){
   $("#TableList").html(t)}):$("#TableList").html('<p class="tac noData">暂无数据,请稍后再试!</p>')},function(t){
   $Common.Popup.LoadingHide("WrapInfo"),$("#TableList").html('<p class="tac noData">暂无数据,请稍后再试!</p>')})}$(function(){
   SetOption_Date()})
</script>

可以看出这里是写了一个请求,就是打开网页后的第二时间再去请求访问一个数据(数据没有存放在网页上),所以我们去看它访问的data是从哪里来的,然后去get那个data就可以了
在这里插入图片描述
但是问题又来了

打开里面XHR文件发现居然数据是加密的
在这里插入图片描述

如何加密一定就在那些JS文件中
先看了一下call stack来判断明文密文可能会在哪里
在这里插入图片描述

就是说,如果某个函数里面已经是明文了,那么就不用去这个函数里面call的下一级函数里面找加密的方法了,因为之后都是明文了,要去上一级找。
最终我们找到了这个加密肯定在蓝色的部分,于是我们再去寻找shell是如何定义的就可以解密。
在这里插入图片描述
之后在console里面进行了操作 搜索到了这个shell在哪里
在这里插入图片描述

点击那个蓝色的webDES.min.js?v=1.0.0:1出现了
在这里插入图片描述

this[_0x2246('0x257', 'nArV')] = function(_0xa0c834) {
   
        var _0x51eedc = {
   
            'pKENi': function _0x2f627(_0x5b6f5a, _0x440924) {
   
                return _0x5b6f5a === _0x440924;
            },
            'wnfPa': 'ZGz',
            'VMmle': '7|1|8|9|5|2|3|6|0|4',
            'GKWFf': function _0x1a4e13(_0x40cfde, _0x16f3c2) {
   
                return _0x40cfde == _0x16f3c2;
            },
            'MUPgQ': function _0x342f0d(_0x19038b, _0x4004d6) {
   
                return _0x19038b >= _0x4004d6;
            },
            'hLXma': function _0x55adaf(_0x45a871, _0x161bdf) {
   
                return _0x45a871 + _0x161bdf;
            },
            'JdOlO': function _0x13e00a(_0x5899a9, _0x4bb34d) {
   
                return _0x5899a9 + _0x4bb34d;
            },
            'qrTpg': function _0x1198fb(_0x55b317, _0x22e1db, _0x1b091a) {
   
                return _0x55b317(_0x22e1db, _0x1b091a);
            },
            'pdmMk': function _0xe2b022(_0x4af286, _0x4c2fd4) {
   
                return _0x4af286 - _0x4c2fd4;
            },
            'xVKWW': function _0x1094a3(_0x5f3627, _0x2a0ac5, _0x3ad2e5) {
   
                return _0x5f3627(_0x2a0ac5, _0x3ad2e5);
            }
        };
        if (_0x51eedc[_0x2246('0x258', '@1Ws')](_0x2246('0x259', 'E&PI'), _0x51eedc['wnfPa'])) {
   
            this['_append'](a);
            return this[_0x2246('0x25a', 'GL3Q')]();
        } else {
   
            var _0x492a62 = _0x51eedc[_0x2246('0x25b', '&59Q')][_0x2246('0x25c', ')q#9')]('|')
              , _0x356b01 = 0x0;
            while (!![]) {
   
                switch (_0x492a62[_0x356b01++]) {
   
                case '0':
                    _0x554c90 = _grsa_JS[_0x2246('0x25d', 'E&PI')]['decrypt']({
   
                        'ciphertext': _grsa_JS['enc'][_0x2246('0x25e', 'sy^o')]['parse'](_0xa0c834)
                    }, _0x2cf8ae, {
   
                        'iv': _0x554c90,
                        'mode': _grsa_JS[_0x2246('0x16c', 'O^50')][_0x2246('0x25f', 'Who^')],
                        'padding': _grsa_JS[_0x2246('0x260', '7IfV')][_0x2246('0x261', 'E&PI')]
                    })[_0x2246('0x1c', 'yY#5')](_grsa_JS['enc'][_0x2246('0x262', ']2BX')]);
                    continue;
                case '1':
                    if (_0x51eedc[_0x2246('0x263', 'Jsmq')](null, _0xa0c834) || _0x51eedc[_0x2246('0x264', '!2eC')](0x10, _0xa0c834['length']))
                        return _0xa0c834;
                    continue;
                case '2':
                    _0xa0c834 = _0x9843d3(_0xa0c834, _0x2cf8ae, 0x8);
                    continue;
                case '3':
                    _0x2cf8ae = _grsa_JS[_0x2246('0x265', 'RQ2o')][_0x2246('0x266', '3j7z')][_0x2246('0x267', 'RQ2o')](_0x554c90);
                    continue;
                case '4':
                    return _0x554c90[_0x2246('0x268', 'cs*4')](0x0, _0x51eedc[_0x2246('0x269', 'MVsm')](_0x554c90[_0x2246('0x26a', '0J6f')]('}'), 0x1));
                case '5':
                    _0x554c90 = _0xa0c834[_0x2246('0x26b', 'UwHa')](_0x2cf8ae, 0x8);
                    continue;
                case '6':
                    _0x554c90 = _grsa_JS[_0x2246('0x26c', '4VZ$')]['Utf8']['parse'](_0x554c90);
                    continue;
                case '7':
                    if (!navigator || !navigator[_0x2246('0x26d', '0I#o')])
                        return '';
                    continue;
                case '8':
                    var _0x554c90 = _0x51eedc[_0x2246('0x26e', 'Yb4P')](_0x51eedc[_0x2246('0x26f', 'BQ5p')](parseInt, _0xa0c834[_0x51eedc[_0x2246('0x270', 'Z2VK')](_0xa0c834['length'], 0x1)], 0x10), 0x9)
                      , _0x2cf8ae = _0x51eedc[_0x2246('0x271', 'yY#5')](parseInt, _0xa0c834[_0x554c90], 0x10);
                    continue;
                case '9':
                    _0xa0c834 = _0x9843d3(_0xa0c834, _0x554c90, 0x1);
                    continue;
                }
                break;
            }
        }
    }
    ;
}
  , webInstace = new webDES();

大家感受一下,这个是加了混淆之后的代码(为了让代码可读性更加差)
然而由于网络上没有找到特别好的解析出反混淆的代码的小程序(甚至大佬还尝试了用一个新的变量来代替这一堆,但是后来还是放弃了…)

—————————————————————————————————
下面是方法二(最终成功了)
在你的Py程序中添加无头浏览器模块
提交网页地址给无头浏览器模块
无头浏览器执行JS,等待加载完成
无头浏览器返回全部网页文本给你的Py程序(这时返回的就是加载后的网页代码)
这种方法比较简单 但是电脑会比较累

参考https://www.cnblogs.com/linxiyue/p/10215912.html

“一般的的静态HTML页面可以使用requests等库直接抓取,但还有一部分比较复杂的动态页面,这些页面的DOM是动态生成的,有些还需要用户与其点击互动,这些页面只能使用真实的浏览器引擎动态解析,Selenium和Chrome Headless可以很好的达到这种目的。”

附上代码

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
import time

chrome_options = webdriver.ChromeOptions()
# 使用headless无界面浏览器模式
chrome_options.add_argument('--headless') #增加无界面选项
chrome_options.add_argument('--disable-gpu') #如果不加这个选项,有时定位会出现问题

chrome_drivers = "/Users/VictoriaShaw/chromedriver"

# 启动浏览器,获取网页源代码
browser = webdriver.Chrome(options=chrome_options, executable_path=chrome_drivers)
mainUrl = "https://www.endata.com.cn/BoxOffice/BO/Year/index.html"
browser.get(mainUrl)
#有些图片需要滚动到才可以加载出来的时候需要这个scroll 
# 本project中想要获取的是里面的数据 不需要图片 以下4行代码可以不要
for i in range(1, 11): 
    browser.execute_script(
        "window.scrollTo(0, document.body.scrollHeight/10*%s);" % i
    )
    time.sleep(0.5) #取决于原页面的加载速度 如果这个sleep里面的时间小雨实际加载出来的时间,那么得到的数据就会不完整
data = browser.page_source
with open("test.out.html", "w") as f:
    f.write(str(data))

之后就得到了一个html文件
里面的代码页面加载后的代码
查看一下果然里面的table是有tr和里面的数据的

        }</script><section class="box-office-bg navgation"><div class
  • 7
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值