<!DOCTYPE html> <html> <head> <metacharset="UTF-8" /> <title>This is a Demo</title> </head> <body> <divid="container"> <divclass="wrapper"> <h2class="title">Hello World</h2> <pclass="text">Hello, this is a paragraph.</p> </div> </div> </body> </html>
这是最基本的 HTML 代码,我们将其保存为一个 test.html 文件,然后把它放在某台具有固定公网 IP 的主机上,主机上装上 Apache 或 Nginx 等服务器,这样这台主机就可以作为服务器了,其他人便可以通过访问服务器看到这个页面,这就搭建了一个最简单的网站。
这种网页的内容是 HTML 代码编写的,文字、图片等内容均通过写好的 HTML 代码来指定,这种页面叫作静态网页。它加载速度快,编写简单,但是存在很大的缺陷,如可维护性差,不能根据 URL 灵活多变地显示内容等。例如,我们想要给这个网页的 URL 传入一个 name 参数,让其在网页中显示出来,是无法做到的。
那么,我们怎样利用 Cookies 保持状态呢?当客户端第一次请求服务器时,服务器会返回一个响应头中带有 Set-Cookie 字段的响应给客户端,用来标记是哪一个用户,客户端浏览器会把 Cookies 保存起来。当浏览器下一次再请求该网站时,浏览器会把此 Cookies 放到请求头一起提交给服务器,Cookies 携带了 Session ID 信息,服务器检查该 Cookies 即可找到对应的 Session 是什么,然后再判断 Session 来辨认用户状态。
在成功登录某个网站时,服务器会告诉客户端设置哪些 Cookies 信息。在后续访问页面时,客户端会把 Cookies 发送给服务器,服务器再找到对应的 Session 加以判断。如果 Session 中的某些设置登录状态的变量是有效的,那就证明用户处于登录状态,此时返回登录之后才可以查看的网页内容,浏览器再进行解析便可以看到了。
反之,如果传给服务器的 Cookies 是无效的,或者 Session 已经过期了,我们将不能继续访问页面,此时可能会收到错误的响应或者跳转到登录页面重新登录。
但是当我们关闭浏览器时,浏览器不会主动在关闭之前通知服务器它将要关闭,所以服务器根本不会有机会知道浏览器已经关闭。之所以会有这种错觉,是因为大部分网站都使用会话 Cookie 来保存 Session ID 信息,而关闭浏览器后 Cookies 就消失了,再次连接服务器时,也就无法找到原来的 Session 了。如果服务器设置的 Cookies 保存到硬盘上,或者使用某种手段改写浏览器发出的 HTTP 请求头,把原来的 Cookies 发送给服务器,则再次打开浏览器,仍然能够找到原来的 Session ID,依旧还是可以保持登录状态的。
HTML 是用来描述网页的一种语言,网页包括文字、按钮、图片和视频等各种复杂的元素,其基础架构就是 HTML。不同类型的元素通过不同类型的标签来表示,如图片用 img 标签表示,视频用 video 标签表示,段落用 p 标签表示,它们之间的布局又常通过布局标签 div 嵌套组合而成,各种标签通过不同的排列和嵌套才形成了网页的框架。
那 HTML 长什么样子呢?我们可以随意打开一个网站,比如淘宝 https://www.taobao.com,然后右键菜单点击“检查元素”或者按 F12 快捷键,即可打开浏览器开发者工具,切换到 Elements 面板,这时候就可以看到这里呈现的就是淘宝网对应的 HTML,它包含了一系列标签,浏览器解析这些标签后,便会在网页中渲染成一个个的节点,这便形成了我们平常看到的网页。比如这里可以看到一个输入框就对应一个 input 标签,可以用于输入文字。
我们首先用例子来感受一下 HTML 的基本结构。新建一个文本文件,名称叫做 test.html,内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
<!DOCTYPE html> <html> <head> <metacharset="UTF-8" /> <title>This is a Demo</title> </head> <body> <divid="container"> <divclass="wrapper"> <h2class="title">Hello World</h2> <pclass="text">Hello, this is a paragraph.</p> </div> </div> </body> </html>
这就是一个最简单的 HTML 实例。开头用 DOCTYPE 定义了文档类型,其次最外层是 html 标签,最后还有对应的结束标签来表示闭合,其内部是 head 标签和 body 标签,分别代表网页头和网页体,它们也需要结束标签。head 标签内定义了一些页面的配置和引用,如:
1
<metacharset="UTF-8" />
它指定了网页的编码为 UTF-8。
title 标签则定义了网页的标题,会显示在网页的选项卡中,不会显示在正文中。body 标签内则是在网页正文中显示的内容。div 标签定义了网页中的区块,它的 id 是 container,这是一个非常常用的属性,且 id 的内容在网页中是唯一的,我们可以通过它来获取这个区块。然后在此区块内又有一个 div 标签,它的 class 为 wrapper,这也是一个非常常用的属性,经常与 CSS 配合使用来设定样式。然后此区块内部又有一个 h2 标签,这代表一个二级标题。另外,还有一个 p 标签,这代表一个段落。在这两者中直接写入相应的内容即可在网页中呈现出来,它们也有各自的 class 属性。
将代码保存后,双击该文件在浏览器中打开,可以看到如图所示的内容。
可以看到,选项卡上显示了 This is a Demo 字样,这是我们在 head 中的 title 里定义的文字。而网页正文是 body 标签内部定义的各个元素生成的,可以看到这里显示了二级标题和段落。
这个实例便是网页的一般结构。一个网页的标准形式是 html 标签内嵌套 head 和 body 标签,head 内定义网页的配置和引用,body 内定义网页的正文。
3 节点树及节点间的关系
在 HTML 中,所有标签定义的内容都是节点,它们构成了一个 HTML 节点树,也称之为 HTML DOM 树。
我们先看下什么是 DOM。DOM 是 W3C(万维网联盟)的标准,其英文全称 Document Object Model,即文档对象模型。它定义了访问 HTML 和 XML 文档的标准。根据 W3C 的 HTML DOM 标准,HTML 文档中的所有内容都是节点。
整个网站文档是一个文档节点。
每个 html 标签对应一个根元素节点,即上例中的 html 标签,这属于一个跟元素节点。
节点内的文本是文本节点,比如 a 节点代表一个超链接,它内部的文本也被认为是一个文本节点。
每个节点的属性是属性节点,比如 a 节点有一个 href 属性,它就是一个属性节点。
注释是注释节点,在 HTML 中有特殊的语法会被解析为注释,但其也会对应一个节点。
所以,HTML DOM 将 HTML 文档视作树结构,这种结构被称为节点树,如图所示:
通过 HTML DOM,树中的所有节点均可通过 JavaScript 访问,所有 HTML 节点元素均可被修改,也可以被创建或删除。
在 CSS 中,我们使用 CSS 选择器来定位节点。例如,上例中 div 节点的 id 为 container,那么就可以表示为 #container,其中 # 开头代表选择 id,其后紧跟 id 的名称。另外,如果我们想选择 class 为 wrapper 的节点,便可以使用.wrapper,这里以点(.)开头代表选择 class,其后紧跟 class 的名称。另外,还有一种选择方式,那就是根据标签名筛选,例如想选择二级标题,直接用 h2 即可。这是最常用的 3 种表示,分别是根据 id、class、标签名筛选,请牢记它们的写法。
另外,CSS 选择器还支持嵌套选择,各个选择器之间加上空格分隔开便可以代表嵌套关系,如 #container .wrapper p 则代表先选择 id 为 container 的节点,然后选中其内部的 class 为 wrapper 的节点,然后再进一步选中其内部的 p 节点。另外,如果不加空格,则代表并列关系,如 div#container .wrapper p.text 代表先选择 id 为 container 的 div 节点,然后选中其内部的 class 为 wrapper 的节点,再进一步选中其内部的 class 为 text 的 p 节点。这就是 CSS 选择器,其筛选功能还是非常强大的。
Slidev is a slides maker and presenter designed for developers, consist of the following features
- 📝 **Text-based** - focus on the content with Markdown, and then style them later - 🎨 **Themable** - theme can be shared and used with npm packages - 🧑💻 **Developer Friendly** - code highlighting, live coding with autocompletion - 🤹 **Interactive** - embedding Vue components to enhance your expressions - 🎥 **Recording** - built-in recording and camera view - 📤 **Portable** - export into PDF, PNGs, or even a hostable SPA - 🛠 **Hackable** - anything possible on a webpage
<br> <br>
Read more about [Why Slidev?](https://sli.dev/guide/why)
You can use Vue components directly inside your slides.
We have provided a few built-in components like `<Tweet/>` and `<Youtube/>` that you can use directly. And adding your custom components is also super easy.
parser = argparse.ArgumentParser(description='Scrape Function') parser.add_argument('url', type=str, help='an integer for the accumulator') parser.add_argument('timeout', type=int, help='sum the integers (default: find the max)')
if __name__ == '__main__': args = parser.parse_args() scrape(args.url, args.timeout)
这样我们才能顺利地使用命令行来调用这个脚本:
1
python3 main.py https://www.baidu.com 10
是不是感觉非常麻烦?argparse 写起来又臭又长,想想就费劲。
Fire
但接下来我们要介绍一个库,用它我们只需要两行代码就可以做到如上操作。
这个库的名字叫做Fire,它可以快速为某个 Python 方法或者类添加命令行的参数支持。
先看看安装方法,使用 pip3 安装即可:
1
pip3 install fire
这样我们就安装好了。
使用
下面我们来看几个例子。
方法支持
第一个代码示例如下:
1 2 3 4 5 6 7
import fire
defhello(name="World"): return"Hello %s!" % name
if __name__ == '__main__': fire.Fire(hello)
这里我们定义了一个 hello 方法,然后接收一个 name 参数,默认值是 World,接着输出了 Hello 加 name 这个字符串。
然后接着我们导入了 fire 这个库,调用它的 Fire 方法并传入 hello 这个方法声明,会发生什么事情呢?
我们把这段代码保存为 demo1.py,接着用 Python3 来运行一下:
1
python3 demo1.py
运行结果如下:
1
Hello World!
看起来并没有什么不同。
但我们这时候如果运行如下命令,就可以看到一些神奇的事情了:
1
python3 demo1.py --help
运行结果如下:
1 2 3 4 5 6 7 8 9
NAME demo1.py
SYNOPSIS demo1.py <flags>
FLAGS --name=NAME Default: 'World'
可以看到,这里它将 name 这个参数转化成了命令行的一个可选参数,我们可以通过 —-name 来替换 name 参数。
我们来试下:
1
python3 demo1.py --name 123
这里我们传入了一个 name 参数是 123,这时候我们就发现运行结果就变成了如下内容:
1
Hello 123!
是不是非常方便?我们没有借助 argparse 就轻松完成了命令行参数的支持和替换。
那如果我们将 name 这个参数的默认值取消呢?代码改写如下:
1 2 3 4 5 6 7
import fire
defhello(name): return"Hello %s!" % name
if __name__ == '__main__': fire.Fire(hello)
这时候重新运行:
1
python3 demo1.py --help
就可以看到结果变成了如下内容:
1 2 3 4 5 6 7 8 9 10 11
NAME demo1.py
SYNOPSIS demo1.py NAME
POSITIONAL ARGUMENTS NAME
NOTES You can also use flags syntax for POSITIONAL ARGUMENTS
这时候我们发现 name 这个参数就变成了必传参数,我们必须在命令行里指定这个参数内容,调用就会变成如下命令:
1
python3 demo1.py 123
运行结果还是一样的。
类支持
当然 fire 这个库不仅仅支持给方法添加命令行的支持,还支持给一个类添加命令行的支持。
下面我们再看一个例子:
1 2 3 4 5 6 7 8
import fire
classCalculator(object): defdouble(self, number): return2 * number
OPTIONS: -p, --port Port to listen (default: 7681, use `0` for random port) -i, --interface Network interface to bind (eg: eth0), or UNIX domain socket path (eg: /var/run/ttyd.sock) -c, --credential Credential for Basic Authentication (format: username:password) -u, --uid User id to run with -g, --gid Group id to run with -s, --signal Signal to send to the command when exit it (default: 1, SIGHUP) -a, --url-arg Allow client to send command line arguments in URL (eg: http://localhost:7681?arg=foo&arg=bar) -R, --readonlyDo not allow clients to write to the TTY -t, --client-option Send option to client (format: key=value), repeat to add more options -T, --terminal-type Terminal type to report, default: xterm-256color -O, --check-originDo not allow websocket connection from different origin -m, --max-clients Maximum clients to support (default: 0, no limit) -o, --once Accept only one client and exit on disconnection -B, --browser Open terminal with the default system browser -I, --index Custom index.html path -b, --base-path Expected base path for requests coming from a reverse proxy (eg: /mounted/here) -P, --ping-interval Websocket ping interval(sec) (default: 300) -6, --ipv6 Enable IPv6 support -S, --ssl Enable SSL -C, --ssl-cert SSL certificate file path -K, --ssl-key SSL key file path -A, --ssl-ca SSL CA file path for client certificate verification -d, --debug Set log level (default: 7) -v, --version Print the version and exit -h, --help Print this text and exit
Visit https://github.com/tsl0922/ttyd to get more information and report bugs.
defrun(self): global l ret = subprocess.Popen( self.command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) for i in iter(ret.stdout.readline, b""): res = i.decode().strip() print(res) l.append(res)
classServerHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): defdo_GET(self): global l if self.path == uri: self.send_response(200) self.send_header('Content-Type', 'text/plain') self.end_headers() self.wfile.write(l)
if __name__ == '__main__': # New Thread: Get Command Result t1 = thread('1', sys.argv[1]) t1.start() # Webserver port = int(sys.argv[2]) print("URL: http://HOST:{0}{1}".format(port, uri)) Handler = ServerHandler httpd = BaseHTTPServer.HTTPServer(('0.0.0.0', port), Handler) httpd.serve_forever()
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.common.exceptions import WebDriverException import time from loguru import logger
COUNT = 1000
for i in range(1, COUNT + 1): try: browser = webdriver.Chrome() wait = WebDriverWait(browser, 10) browser.get('https://captcha1.scrape.center/') button = wait.until(EC.element_to_be_clickable( (By.CSS_SELECTOR, '.el-button'))) button.click() captcha = wait.until( EC.presence_of_element_located((By.CSS_SELECTOR, '.geetest_slicebg.geetest_absolute'))) time.sleep(5) captcha.screenshot(f'data/captcha/images/captcha_{i}.png') except WebDriverException as e: logger.error(f'webdriver error occurred {e.msg}') finally: browser.close()
with sync_playwright() as p: for browser_type in [p.chromium, p.firefox, p.webkit]: browser = browser_type.launch(headless=False) page = browser.new_page() page.goto('https://www.baidu.com') page.screenshot(path=f'screenshot-{browser_type.name}.png') print(page.title()) browser.close()
Options: -o, --output <file name> saves the generated script to a file --target <language> language to use, one of javascript, python, python-async, csharp (default: "python") -b, --browser <browserType> browser to use, one of cr, chromium, ff, firefox, wk, webkit (default: "chromium") --channel <channel> Chromium distribution channel, "chrome", "chrome-beta", "msedge-dev", etc --color-scheme <scheme> emulate preferred color scheme, "light" or "dark" --device <deviceName> emulate device, for example "iPhone 11" --geolocation <coordinates> specify geolocation coordinates, for example "37.819722,-122.478611" --load-storage <filename> load context storage state from the file, previously saved with --save-storage --lang <language> specify language / locale, for example "en-GB" --proxy-server <proxy> specify proxy server, for example "http://myproxy:3128" or "socks5://myproxy:8080" --save-storage <filename> save context storage state at the end, for later use with --load-storage --timezone <time zone> time zone to emulate, for example "Europe/Rome" --timeout <timeout> timeout for Playwright actions in milliseconds (default: "10000") --user-agent <ua string> specify user agent string --viewport-size <size> specify browser viewport size in pixels, for example "1280, 720" -h, --help display help for command
with sync_playwright() as p: browser = p.chromium.launch(headless=False) page = browser.new_page() page.goto('https://spa6.scrape.center/') page.wait_for_load_state('networkidle') elements = page.query_selector_all('a.name') for element in elements: print(element.get_attribute('href')) print(element.text_content()) browser.close()
/detail/ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWIx 霸王别姬 - Farewell My Concubine /detail/ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWIy 这个杀手不太冷 - Léon /detail/ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWIz 肖申克的救赎 - The Shawshank Redemption /detail/ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWI0 泰坦尼克号 - Titanic /detail/ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWI1 罗马假日 - Roman Holiday /detail/ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWI2 唐伯虎点秋香 - Flirting Scholar /detail/ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWI3 乱世佳人 - Gone with the Wind /detail/ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWI4 喜剧之王 - The King of Comedy /detail/ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWI5 楚门的世界 - The Truman Show /detail/ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWIxMA== 狮子王 - The Lion King
I am happy to see that Python is so widely used in the Chinese IT community. I hope this book will help more people understand Python and web crawling/scraping. *
—Guido van Rossum, creator of Python, Distinguished Engineer, Microsoft
嗯,总之,经过我的一些尝试之后,就感觉 —— 为自己制定短期的目标和规划真的很有帮助,这个短期的目标和规划时间段可能在一个月或者几周,先想这个月或者几周应该去做些什么,然后细化到每天应该去做些什么,这个一定要列详细,不要空洞,然后最好还能标记好优先级。就比如说,我规划一个月可能要学一门课程,那么我就分配一下,我哪一天需要具体学从第几节到第几节的内容,记录到我的 Todo List 里面。我是用的「滴答清单」这款软件,在里面我就给我每天需要做的事情做好分配,这样每天我就知道自己需要做什么了。
接下来,可以选用任意一台带有公网 IP 的主机来配置负载均衡。首先,在这台主机上装好 Nginx,然后修改 Nginx 的配置文件 nginx.conf,添加如下内容:
1 2 3 4 5 6 7 8 9 10 11 12 13
http { upstream splash { least_conn; server 41.159.27.223:8050; server 41.159.27.221:8050; server 41.159.27.9:8050; server 41.159.117.119:8050; } server { listen 8050; location / {proxy_pass http://splash;} } }
upstream splash { server 41.159.27.223:8050 weight=4; server 41.159.27.221:8050 weight=2; server 41.159.27.9:8050 weight=2; server 41.159.117.119:8050 weight=1; }
import requests from urllib.parse import quote import re
lua = ''' function main(splash, args) local treat = require("treat") local response = splash:http_get("http://httpbin.org/get") return treat.as_string(response.body) end '''
IDA Pro 的英文全称是 Interactive Disassembler Professional,即交互式反汇编器专业版,大家也称 之为 IDA。它由一家总部位于比利时的 Hex-Rayd 公司开发,功能十分强大,是目前流行的反汇编软 件之一,也是安全分析人士必备的一款软件。
IDA Pro 最重要的功能便是可以将二进制文件中的机器代码(如 010101)转化成汇编代码,甚至 可以进一步根据汇编代码的执行逻辑还原出高级语言(如 C/C++)编写的代码,从而大大提高代码的 可读性。IDA Pro 不仅仅局限于分析 Android 中的 so 文件,它可以处理和分析几乎所有的二进制文件, Windows、DOS、Unix、Linux、Mac、Java、.NET 等平台的二进制文件都不在话下。另外,IDA Pro 提 供了图形界面和强大的调试功能,利用它我们可以直观地实时调试和分析二进制文件。除了这些,IDA Pro 还提供开放式的插件架构,我们可以编写自定义的插件轻松扩展其功能。
总之,IDA Pro 是一款极其强大的反汇编软件,已经成为业界安全分析必不可少的一个工具,更多介绍可以查看 IDA Pro 的官网。
安装
IDA Pro 是收费的,但是有不少大佬已经破解了,可以移步相关资源查看:https://bbs.pediy.com/thread-263559.htm。