【问题标题】:Can't access site programmatically无法以编程方式访问站点
【发布时间】:2023-02-03 05:12:54
【问题描述】:

我正在尝试从 dtek-kem.com.ua/ua/shutdowns 获取关机列表 list 但是当我发送一个得到通过 python 请求,我得到响应:请求不成功,Incapsula 事件 ID:... 我也知道这个网站使用 imperva security

使用 python aiohttp 发送请求:

method='GET'
Host: www.dtek-kem.com.ua
accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
accept-encoding: gzip, deflate, br
accept-language: en,ru;q=0.9,uk;q=0.8,en-US;q=0.7
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36
cache-control: max-age=0
sec-ch-ua: "Not?A_Brand";v="8", "Chromium";v="108", "Google Chrome";v="108"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
sec-fetch-dest: document
sec-fetch-mode: navigate
sec-fetch-site: same-origin
sec-fetch-user: ?1
upgrade-insecure-requests: 1

我收到以下回复:

https://www.dtek-kem.com.ua/ua/shutdowns [200 OK]
Content-Type: text/html
Cache-Control: no-cache, no-store
Connection: close
Content-Length: 899
X-Iinfo: 4-43048402-0 0NNN RT(1670585645218 54) q(0 -1 -1 -1) r(0 -1) B12(4,316,0) U2
Strict-Transport-Security: max-age=31536000; includeSubDomains
Set-Cookie: incap_ses_287_2224657=4b9AWuO2/2fTOuVPWqH7Ay0dk2MAAAAAtnXLv3+84L80QP1nTKP8Fg==; Domain=dtek-kem.com.ua; Path=/; SameSite=None; Secure
Set-Cookie: visid_incap_2224657=OOVTSrqKRCeH0QB7kzrgIC0dk2MAAAAAQUIPAAAAAAB47Nowjvq7LxL76cUkJG0a; Domain=dtek-kem.com.ua; expires=Fri, 08 Dec 2023 22:17:56 GMT; HttpOnly; Path=/; SameSite=None; Secure

和 html 内容:

<html style="height:100%">
 <head>
  <meta content="NOINDEX, NOFOLLOW" name="ROBOTS"/>
  <meta content="telephone=no" name="format-detection"/>
  <meta content="initial-scale=1.0" name="viewport"/>
  <meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible"/>
  <script async="" src="/Physicken-Like-my-Hath-I-haue-ster-Banq-All-bids">
  </script>
 </head>
 <body style="margin:0px;height:100%">
  <iframe frameborder="0" height="100%" id="main-iframe" marginheight="0px" marginwidth="0px" src="/_Incapsula_Resource?SWUDNSAI=31&amp;xinfo=4-43048402-0%200NNN%20RT%281670585645218%2054%29%20q%280%20-1%20-1%20-1%29%20r%280%20-1%29%20B12%284%2c316%2c0%29%20U2&amp;incident_id=287000410527500428-206407667178998340&amp;edet=12&amp;cinfo=04000000&amp;rpinfo=0&amp;cts=swfgpEczXy9hSsxHaaLf43gsGYhnGBhKA1jABnA0Ljuov3FUOG0mGjfE6li1tAg6&amp;mth=GET" width="100%">
   Request unsuccessful. Incapsula incident ID: 287000410527500428-206407667178998340
  </iframe>
 </body>
</html>

我通过浏览器访问站点并选择第一个数据包发送到服务器,从网络选项卡中完全复制了请求的标头 first packet send 这样做时,我从服务器得到不同的响应。服务器不会收到完全相同的请求吗? 来自浏览器请求的响应:

access-control-allow-credentials: true
access-control-allow-credentials: true
access-control-allow-headers: DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type
access-control-allow-headers: DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type
access-control-allow-methods: GET, POST, OPTIONS
access-control-allow-methods: GET, POST, OPTIONS
access-control-allow-origin: https://admin.dtek-kem.com.ua
cache-control: no-store, no-cache, must-revalidate
cache-control: max-age=900
cache-control: public, max-age=900
cache-control: no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0
content-encoding: gzip
content-type: text/html; charset=UTF-8
date: Fri, 09 Dec 2022 12:02:38 GMT
expect-ct: enforce; max-age=3600
expect-ct: enforce; max-age=3600
expires: Thu, 19 Nov 1981 08:52:00 GMT
pragma: no-cache
referrer-policy: strict-origin-when-cross-origin
server: nginx
path=/; secure; secure; HttpOnly
status: 200
httpVersion: http/2.0
cookies: [{'name': 'dtek-kem', 'value': '0mspqled433d6pq7t9q9ttcjos'}, {'name': '_csrf-dtek-kem', 'value': '0957f055f621ade8b7c6a5136201e0081a1579972aa33443a65646c44afeb161a%3A2%3A%7Bi%3A0%3Bs%3A14%3A%22_csrf-dtek-kem%22%3Bi%3A1%3Bs%3A32%3A%22aJodoGWonH3u7fdI7jVzex4n6yBPZ9qX%22%3B%7D'}, {'name': 'Domain', 'value': 'dtek-kem.com.ua'}, {'name': 'incap_wrt_356', 'value': '3iOTYwAAAAA3Gkt0FwAI5AIQxJuq1AEYicrMnAYgAijdx8ycBknxuwb65PIpngUwOmGF+xE='}]
content: {'size': 635168, 'mimeType': 'text/html'}

我要进入一个大主题吗“绕过防火墙”或者我错过了什么

【问题讨论】:

  • 听起来像是维护该网站的人实施了一些措施来阻止自动访问
  • @NicoHaase 但如果两个请求相同
  • “大主题“绕过防火墙””——这是什么意思?
  • @NicoHaase 我的意思是我的问题类似于“如何破解微软”
  • @f211 对这个网站的请求对我来说很好。你打算怎么拿到桌子?它被渲染了。此外,它是为您从https://www.dtek-kem.com.ua/ua/ajax 获得的特定房屋和街道渲染的。那么,您想要在哪条街道和哪栋房子里购买哪张桌子?

标签: html parsing python-requests get-request web-application-firewall


【解决方案1】:

要求

如果您将“incap_ses_1612_2224657”cookie 传递给会话,请求工作正常:

import requests
import urllib.parse
from bs4 import BeautifulSoup as bs

url = r'https://www.dtek-kem.com.ua'
s = requests.Session()
s.cookies['incap_ses_1612_2224657'] = 'oRiXXtkFuiaomXJJnfleFu98mGMAAAAACfnEff2NJ+ZJhjCB4Sr2Zw=='
r = s.get(urllib.parse.urljoin(url, 'ua/shutdowns'))
soup = bs(r.content, 'lxml')

所以这不是一个像“绕过防火墙“,该站点非常好。此外,通过简单地更新页面,在浏览器中绕过了 reCAPTCHAF5.只要会话处于活动状态,就可以从那里获取 Cookie 并使用一段时间。
但是我不知道如何单独使用 requests 获取它,有时它会自己获取完整的 cookie,标头并不重要。

做一张桌子

现在,我们如何在不使用渲染和诸如 Scrapydryscraperequests_html 和其他很酷但资源繁重的库的情况下准备表格?
在某些情况下,这些会有所帮助,但这里可以使用 甚至 单独获取数据。我们只需要网页中包含所有所需信息的单个&lt;script&gt; 元素。

获取表数据

import re
import json

d = soup.find_all(lambda tag: tag.name == 'script' and not tag.attrs)[-1].decode_contents()
d_parsed = {}
for i in re.findall(r'(?<=DisconSchedule.)(w+)(?:s=s)(.+)',d):
    d_parsed[i[0]] = json.loads(i[1])
d = d_parsed

现在 d 变量包含一个字典对象,其中包含街道名称、当前星期几和包含表值的数据,这些表值代表某种需要进一步解析的 3 维表。
但首先我们需要通过 post 请求获取房屋信息:

csrf = soup.find('meta', {'name': 'csrf-token'})['content']
headers = {
    'X-CSRF-Token': csrf,
    'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
}
body = 'method=getHomeNum&data[0][name]=street&data[0][value]='+d['streets'][193]
r = s.post(urllib.parse.urljoin(url, '/ua/ajax'), body.encode('utf-8'), headers=headers)
house = json.loads(r.content)['data']['20']
house
输出:
{'sub_type': 'Застосування стабілізаційних графіків',
 'start_date': '1670926920',
 'end_date': '16:00 13.12.2022',
 'type': '2',
 'sub_type_reason': ['1']}

在这里我们肯定需要一些标题。指定内容类型并传递 标记。 Cookie 已经在会话中。此查询的正文包含街道名称 d['streets'][193] 是 'вул。 Газопровідна'.
响应有一些有用的信息,这些信息在带有黄色背景的表格上方的 div 中呈现。所以,值得拥有。

但是我们正在寻找的是“sub_type_reason”。这就是我所说的第三维。它显示在门牌号的右边,代表“Група”1 / 2 / 3。有时可能会有更多组。

对于这个特定地址“вул. Газопровідна 20”,我们将使用第 1 组。

建表

我将为此使用。我们将进一步做一些修改,所以 pandas 在这种情况下会很棒。

gr = house['sub_type_reason'][0]
df = pd.DataFrame({int(k):d['preset']['data'][gr][k].values() for k in d['preset']['days'].keys()})
df
输出:

    1       2       3       4       5       6       7
0   no      maybe   no      no      maybe   no      no
1   no      maybe   yes     no      maybe   yes     no
2   no      maybe   yes     no      maybe   yes     no
3   no      no      maybe   no      no      maybe   no
4   yes     no      maybe   yes     no      maybe   yes
5   yes     no      maybe   yes     no      maybe   yes
6   maybe   no      no      maybe   no      no      maybe
7   maybe   yes     no      maybe   yes     no      maybe
8   maybe   yes     no      maybe   yes     no      maybe
9   no      maybe   no      no      maybe   no      no
10  no      maybe   yes     no      maybe   yes     no
11  no      maybe   yes     no      maybe   yes     no
12  no      no      maybe   no      no      maybe   no
13  yes     no      maybe   yes     no      maybe   yes
14  yes     no      maybe   yes     no      maybe   yes
15  maybe   no      no      maybe   no      no      maybe
16  maybe   yes     no      maybe   yes     no      maybe
17  maybe   yes     no      maybe   yes     no      maybe
18  no      maybe   no      no      maybe   no      no
19  no      maybe   yes     no      maybe   yes     no
20  no      maybe   yes     no      maybe   yes     no
21  no      no      maybe   no      no      maybe   no
22  yes     no      maybe   yes     no      maybe   yes
23  yes     no      maybe   yes     no  maybe   yes

好,太棒了!
基本上,这是您在网站上看到的同一张表格,但没有电力图标,并且像在移动版本中查看的那样进行了转置。
d['preset']['time_type']:

{'yes': 'Світло є', 'maybe': 'Можливо відключення', 'no': 'Світла немає'}

修改表

根据你的screenshot,这是你想要得到的东西。据我了解,它是关于将“是”和“可能”值折叠成一行重叠时间段。
这很有挑战性,但可以做到。

from operator import itemgetter
from itertools import groupby

row = ['']*len(df.columns)
df = df.replace(['no'],'').replace(['yes','maybe'],True)
collapsed_df = pd.DataFrame(columns=df.columns)
for col_ix, col in enumerate(df.columns):
    for k,g in groupby(enumerate(df.groupby(df[col], axis=0).get_group(True)[col].index), lambda x: x[0]-x[1]):
        intervals = list(map(itemgetter(1), g))
        interval = pd.Interval(intervals[0], intervals[-1]+1, closed='both')
        if interval not in collapsed_df.index:
            collapsed_df.loc[interval] = list(row)
        collapsed_df.loc[interval].iloc[col_ix] = True
df = collapsed_df.sort_index()
df
输出:
            1       2       3       4       5       6       7
[0, 3]              True                    True        
[1, 6]                      True                    True    
[4, 9]      True                    True                    True
[7, 12]             True                    True        
[10, 15]                    True                    True    
[13, 18]    True                    True                    True
[16, 21]            True                    True        
[19, 24]                    True                    True    
[22, 24]    True                    True                    True

我不打算详细描述折叠列背后的魔力,因为答案太长了。而且我非常确定这段代码可以做得更好。
简而言之,我遍历每一行以找到连续值组并折叠它们的索引。折叠索引被转换为间隔,并将真实值添加到具有相应间隔的行。行是在第一次出现时创建的,具有空值。

无论如何,完成了。
它与您的屏幕截图具有相同的输出,但数据不同,因为我们在不同的日子,而且到目前为止数据已经改变。
现在剩下的就是将代表小时间隔的索引值转换为小时字符串,更改列并美化表格以描绘您的屏幕截图。

最后的接触

  • 下载图像并将其编码为 base64
  • &lt;img&gt;标签和二进制源替换真值
  • 将索引转换为字符串类型时间段
  • 分配列名
  • 输入一个索引名称,这里我使用df.columns.name否则,通过命名索引,表头将有两行
  • 设计表格
    • 折叠表格,添加灰色边框并更改字体大小
    • 为页眉背景着色,将文本显示为黑色
    • 如屏幕截图所示,用一条线将“Γодини”与周名分开
    • 在列之间添加边框,更改单元格大小
    • 调整字体粗细
    • 将当前工作日设为粗体
    • 更改图标大小
    • 设置填充单元格的背景颜色
from base64 import b64encode

img = {
    'maybe': b64encode(s.get(urllib.parse.urljoin(url,'media/page/maybe-electricity.png')).content),
    'no': b64encode(s.get(urllib.parse.urljoin(url,'media/page/no-electricity.png')).content)
df = df.replace(True, '<img src="data:image/webp;base64,'+re.sub(r"^b'|'$",'',str(img['no']))+'"></img>')

df.index = ['{:02d}:00 – {:02d}:00'.format(i.left, i.right) for i in df.index]
df.columns = ['Пн','Вт','Ср','Чт','Пт','Сб','Нд']
df.columns.name = 'Години'

styled_df = df.style.set_table_styles([
    {'selector': '',
    'props': [
        ('border-collapse', 'collapse'),
        ('border', '1px solid #cfcfcf'),
        ('font-size', '20px')
    ]},
    {'selector': 'thead tr',
    'props': [
        ('background-color', '#ffe500'),
        ('color', 'black'),
        ('height', '70px')
    ]},
    {'selector': 'thead tr th:first-child',
    'props': [
        ('border', '1px solid #cfcfcf'),
        ('width', '240px')
    ]},
    {'selector': 'td',
    'props': [
        ('border-left', '1px solid #cfcfcf'),
        ('text-align', 'center'),
        ('width', '95px'),
        ('height', '56px')
    ]},
    {'selector': 'td, th',
    'props': [
        ('font-weight', 'lighter')
    ]},
    {'selector': 'thead tr th:nth-child({})'.format(d['currentWeekDayIndex']+1),
    'props': [
        ('font-weight', 'bold')
    ]},
    {'selector': 'img',
    'props': [
        ('height', '23px'),
        ('width', '21px')
    ]},
        {'selector': 'td:has(> img)',
    'props': [
        ('background-color', '#f4f4f4')
    ]}
])
}

styled_df.to_html(escape=False, border=0, encoding='utf-8')
输出:

const image_bin = "data:image/webp;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAACxLAAAsSwGlPZapAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAQQSURBVHgB1ZtNUhNBFIBfTyjLYuPAAbTZuRO3llDhBrh0BdwAbgA3wBMYlq7QEziCZbkDT2CXunAFs5NCSPs6w2ggPzPv9XuT8VvAkDRJ5uvXf687BoZIbbebuywDJql9tgwwtw6qXPZy99mBEKa8SO3qBv7RM+B7Z+54C4igPGugfxIuQZfcw9Va7j6dggBJ+FHefLj2YDYX7cprINPfBv2bh+I95KIsSe3zbnnzJTwJ/gk0hs9BiCR3HzMUcDDyFgQJGP6pAdOFxvAi4R8YNIEzd7QZKwER+1BVhEoDIZLyIkYCjhzYMSVr0IgEn4EgyfAf/4MEbGpfQJDk7gNtl9AH/x4EScY92G4Jv3UjoKSlEpzkLDCQTHuybRI8gGjtB5KqAu2SIDsCBCoFBNojoZOBMIZSeNGu9jAMN0ZfpHoBVcwW+6EHXwYe+bk7WgBhakVAyWwjwauMKiQBgVlJwNf/AAqQBQRmI6GfgQIsAYE+XO2Pe1xPwlw7msA/Ot1JzyhIOA3lQQHSKDDMgl05xH9fn/7iUqODzzx09qAmlLxmhIDVr/jLVpVraIi8i8PoelonalhNoMj+Vt98YEbTZgtwvVmnILMP6JBqajYSzHmdUiwBCSP/17AEh/sHteYNLAE4HWZlgJuS4OFyre6ymSwgdFgQ0Vnpzxj9DiVnwIiA6y5EoiUBh7RXuTveBwIMATL5fwUJrg/JLhBhCJDbARKUkBftnj5bJAnQ2AGSkICd8jY3V0iNAKmZ2i1iJGC738vd0QEwIQq4VFmQBJgSHC7NdyGCDqXwRf7953z6aAmUIgHrc3k+fWh/5d/ewcTP4C7up0tvcEH+GPcIXl7kP6IqhbUYClvq9Usbi/0Gaaude0iDA3s1WJfUrqyjgEMg0pSEiIRIXQyrufBPqtBQF2Ai5g1NSFAX4CPPDWlLaCACTPSIoSlBVcDNyjEqAkq0JGhHgOh8QUOCsoBr8QmTtARlAcZCBCG5IXB6bSrKAvhDYJncEDrCNxFVATgCcDvAW8kNTQlt7ATHJje0JKgJuNk8ITMtuaEhQTECOhaIFO1+enJDWoKiAPIMMCQ3tusUlJSgJoC4CHKh3RPKi0lQE0BZBGGaa4eT1JSQoBgB9ZpAkdTM3gKTWAkqGaGb/f7K3Vmc6WU42SGF/iS4R/i0IqBO7TtMaoqlvLiRoCSgehHk4eqF9MFnjgQlAcZOfXbQ7mW+9nYXqgQlAZOHQD9ok3GbGVVQJKgImLIIctju96AB6kpotBOknNyQoI4E8WEwLIIMzJ2Mvmk4uUE7vCDFpCEy7DM2sDHCO7khybhIwArZCgcqFQTcczBo639hndyQZlhCcfPHvXCtNRO0xVki8wC7mQOtc74cwsbu8DdP/wD8xrK5i7NwCwAAAABJRU5ErkJggg=="
var images = document.getElementsByTagName("img")
for (var i = 0; i < images.length; i++) {
    images[i].src = image_bin;
}
#T_b04e1  {
  border-collapse: collapse;
  border: 1px solid #cfcfcf;
  font-size: 20px;
}
#T_b04e1 thead tr {
  background-color: #ffe500;
  color: black;
  height: 70px;
}
#T_b04e1 thead tr th:first-child {
  border: 1px solid #cfcfcf;
  width: 240px;
}
#T_b04e1 td {
  border-left: 1px solid #cfcfcf;
  text-align: center;
  width: 95px;
  height: 56px;
}
#T_b04e1 td {
  font-weight: lighter;
}
#T_b04e1  th {
  font-weight: lighter;
}
#T_b04e1 thead tr th:nth-child(3) {
  font-weight: bold;
}
#T_b04e1 img {
  height: 23px;
  width: 21px;
}
#T_b04e1 td:has(> img) {
  background-color: #f4f4f4;
}
<table id="T_b04e1">
  <thead>
    <tr>
      <th class="index_name level0" >Години</th>
      <th id="T_b04e1_level0_col0" class="col_heading level0 col0" >Пн</th>
      <th id="T_b04e1_level0_col1" class="col_heading level0 col1" >Вт</th>
      <th id="T_b04e1_level0_col2" class="col_heading level0 col2" >Ср</th>
      <th id="T_b04e1_level0_col3" class="col_heading level0 col3" >Чт</th>
      <th id="T_b04e1_level0_col4" class="col_heading level0 col4" >Пт</th>
      <th id="T_b04e1_level0_col5" class="col_heading level0 col5" >Сб</th>
      <th id="T_b04e1_level0_col6" class="col_heading level0 col6" >Нд</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th id="T_b04e1_level0_row0" class="row_heading level0 row0" >00:00 – 03:00</th>
      <td id="T_b04e1_row0_col0" class="data row0 col0" ></td>
      <td id="T_b04e1_row0_col1" class="data row0 col1" ><img></img></td>
      <td id="T_b04e1_row0_col2" class="data row0 col2" ></td>
      <td id="T_b04e1_row0_col3" class="data row0 col3" ></td>
      <td id="T_b04e1_row0_col4" class="data row0 col4" ><img></img></td>
      <td id="T_b04e1_row0_col5" class="data row0 col5" ></td>
      <td id="T_b04e1_row0_col6" class="data row0 col6" ></td>
    </tr>
    <tr>
      <th id="T_b04e1_level0_row1" class="row_heading level0 row1" >01:00 – 06:00</th>
      <td id="T_b04e1_row1_col0" class="data row1 col0" ></td>
      <td id="T_b04e1_row1_col1" class="data row1 col1" ></td>
      <td id="T_b04e1_row1_col2" class="data row1 col2" ><img></img></td>
      <td id="T_b04e1_row1_col3" class="data row1 col3" ></td>
      <td id="T_b04e1_row1_col4" class="data row1 col4" ></td>
      <td id="T_b04e1_row1_col5" class="data row1 col5" ><img></img></td>
      <td id="T_b04e1_row1_col6" class="data row1 col6" ></td>
    </tr>
    <tr>
      <th id="T_b04e1_level0_row2" class="row_heading level0 row2" >04:00 – 09:00</th>
      <td id="T_b04e1_row2_col0" class="data row2 col0" ><img></img></td>
      <td id="T_b04e1_row2_col1" class="data row2 col1" ></td>
      <td id="T_b04e1_row2_col2" class="data row2 col2" ></td>
      <td id="T_b04e1_row2_col3" class="data row2 col3" ><img></img></td>
      <td id="T_b04e1_row2_col4" class="data row2 col4" ></td>
      <td id="T_b04e1_row2_col5" class="data row2 col5" ></td>
      <td id="T_b04e1_row2_col6" class="data row2 col6" ><img></img></td>
    </tr>
    <tr>
      <th id="T_b04e1_level0_row3" class="row_heading level0 row3" >07:00 – 12:00</th>
      <td id="T_b04e1_row3_col0" class="data row3 col0" ></td>
      <td id="T_b04e1_row3_col1" class="data row3 col1" ><img></img></td>
      <td id="T_b04e1_row3_col2" class="data row3 col2" ></td>
      <td id="T_b04e1_row3_col3" class="data row3 col3" ></td>
      <td id="T_b04e1_row3_col4" class="data row3 col4" ><img></img></td>
      <td id="T_b04e1_row3_col5" class="data row3 col5" ></td>
      <td id="T_b04e1_row3_col6" class="data row3 col6" ></td>
    </tr>
    <tr>
      <th id="T_b04e1_level0_row4" class="row_heading level0 row4" >10:00 – 15:00</th>
      <td id="T_b04e1_row4_col0" class="data row4 col0" ></td>
      <td id="T_b04e1_row4_col1" class="data row4 col1" ></td>
      <td id="T_b04e1_row4_col2" class="data row4 col2" ><img></img></td>
      <td id="T_b04e1_row4_col3" class="data row4 col3" ></td>
      <td id="T_b04e1_row4_col4" class="data row4 col4" ></td>
      <td id="T_b04e1_row4_col5" class="data row4 col5" ><img></img></td>
      <td id="T_b04e1_row4_col6" class="data row4 col6" ></td>
    </tr>
    <tr>
      <th id="T_b04e1_level0_row5" class="row_heading level0 row5" >13:00 – 18:00</th>
      <td id="T_b04e1_row5_col0" class="data row5 col0" ><img></img></td>
      <td id="T_b04e1_row5_col1" class="data row5 col1" ></td>
      <td id="T_b04e1_row5_col2" class="data row5 col2" ></td>
      <td id="T_b04e1_row5_col3" class="data row5 col3" ><img></img></td>
      <td id="T_b04e1_row5_col4" class="data row5 col4" ></td>
      <td id="T_b04e1_row5_col5" class="data row5 col5" ></td>
      <td id="T_b04e1_row5_col6" class="data row5 col6" ><img></img></td>
    </tr>
    <tr>
      <th id="T_b04e1_level0_row6" class="row_heading level0 row6" >16:00 – 21:00</th>
      <td id="T_b04e1_row6_col0" class="data row6 col0" ></td>
      <td id="T_b04e1_row6_col1" class="data row6 col1" ><img></img></td>
      <td id="T_b04e1_row6_col2" class="data row6 col2" ></td>
      <td id="T_b04e1_row6_col3" class="data row6 col3" ></td>
      <td id="T_b04e1_row6_col4" class="data row6 col4" ><img></img></td>
      <td id="T_b04e1_row6_col5" class="data row6 col5" ></td>
      <td id="T_b04e1_row6_col6" class="data row6 col6" ></td>
    </tr>
    <tr>
      <th id="T_b04e1_level0_row7" class="row_heading level0 row7" >19:00 – 24:00</th>
      <td id="T_b04e1_row7_col0" class="data row7 col0" ></td>
      <td id="T_b04e1_row7_col1" class="data row7 col1" ></td>
      <td id="T_b04e1_row7_col2" class="data row7 col2" ><img></img></td>
      <td id="T_b04e1_row7_col3" class="data row7 col3" ></td>
      <td id="T_b04e1_row7_col4" class="data row7 col4" ></td>
      <td id="T_b04e1_row7_col5" class="data row7 col5" ><img></img></td>
      <td id="T_b04e1_row7_col6" class="data row7 col6" ></td>
    </tr>
    <tr>
      <th id="T_b04e1_level0_row8" class="row_heading level0 row8" >22:00 – 24:00</th>
      <td id="T_b04e1_row8_col0" class="data row8 col0" ><img></img></td>
      <td id="T_b04e1_row8_col1" class="data row8 col1" ></td>
      <td id="T_b04e1_row8_col2" class="data row8 col2" ></td>
      <td id="T_b04e1_row8_col3" class="data row8 col3" ><img></img></td>
      <td id="T_b04e1_row8_col4" class="data row8 col4" ></td>
      <td id="T_b04e1_row8_col5" class="data row8 col5" ></td>
      <td id="T_b04e1_row8_col6" class="data row8 col6" ><img></img></td>
    </tr>
  </tbody>
</table>

输出是 styled_df.to_html() 输出的复制粘贴,因此它是完全生成的。
我只添加了一个小的 js 代码来通过 &lt;img src=""&gt; 分发重复的图像二进制文件以保存此答案中的字符。 这是制作 sn-p 时唯一手动完成的事情,如果需要,您可以使用正则表达式或其他方式自动完成。

通过添加 buf 可以将输出保存到文件中:

styled_df.to_html(buf='lovely_table.html', escape=False, border=0, encoding='utf-8')

您现在可以尝试折叠列并在“是”和“可能”上分别进行操作以获得适合您需要的不同结果。

【讨论】:

    【解决方案2】:

    我可以解决你的问题。这是一个防火墙。如果您使用固定的 cookie,它无法使您的程序自动化。如果您需要我的帮助,请联系我的邮箱:ciwei0@vip.qq.com

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-23
      • 1970-01-01
      • 2012-01-06
      • 2014-07-29
      相关资源
      最近更新 更多