Socket爬虫:Python版
简述:较为底层的爬虫实现,用于了解爬虫底层实现的具体流程,现在各种好用的爬虫库(如requests,httpx...等)都是基于此进行封装的。
PS:本文只作为实现请求的代码记录,基础部分不做过多阐述。
一、什么是socket
简称:套接字
大白话陈述一下:网络由类似无数终端(计算机设备)组成,每一台计算机设备上面都有很多的程序,而每个程序都有其端口号,ip+端口号形成唯一标识,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作。PS:个人总结的,可能不是很准确看看就行
二、socket实现http请求
http请求方式:get和post
要使用socket首先要实现其实例化:scoket(family,type[,protocal])
其中第一个参数family表示地址族,常用的协议族有:AF_INET、AF_INET6、AF_LOCAL、AF_ROUTE等,默认值为AF_INET,通常使用这个即可。
第二个参数表示Socket类型,这里使用的值有三个:SOCK_STREAM、SOCK_DGRAM、SOCK_RAW。
SOCK_STREAM:TCP类型,保证数据顺序以及可靠性;
SOCK_DGRAM:UDP类型,不保证数据接收的顺序,非可靠连接;
SOCK_RAW:原始类型,允许对底层协议如IP、ICMP进行直接访问,基本不会用到。
默认值是第一个。
第三个参数是指定协议,这个是可选的,通常赋值为0,由系统选择。
GET /article-types/6/ HTTP/1.1
Host: www.zhangdongshengtech.com
Connection: close
第一行:请求方式+空格+请求的资源地址+空格+http协议版本
第二行:明确host
第三行:定义了Connection的值是close,如果不定义,默认是keep-alive。
PS:这里Connection设置为close是为了方便获取完响应后直接断开连接,如果是keep-alive再获取完响应数据后不会断开连接。
报文示例如下:目标url=http://www.baidu.com
GET / HTTP/1.1\r\nHost: www.baidu.com\r\nConnection: close\r\n\r\n
报文解析
一个HTTP请求报文由请求行(request line)、请求头部(header)、空行和请求数据4个部分组成
请求行:由请求方法字段、URL字段和HTTP协议版本字段3个字段组成,它们用空格分隔。例如,GET /index.html HTTP/1.1
请求头部:请求头部由关键字/值对组成,每行一对,关键字和值用英文冒号“:”分隔。例如,User-Agent:产生请求的浏览器类型
空行:最后一个请求头之后是一个空行,发送回车符和换行符,通知服务器以下不再有请求头。
请求数据:请求数据不在GET方法中使用,而是在POST方法中使用。POST方法适用于需要客户填写表单的场合。与请求数据相关的最常使用的请求头是Content-Type和Content-Length。
GET:示例
# -*- coding: utf-8 -*-
# @Time : 2023/1/8 15:12
# @Author : 红后
# @Email : not_enabled@163.com
# @blog : https://www.cnblogs.com/Red-Sun
# @File : socket_http_get.py
# @Software: PyCharm
import socket
url = 'www.baidu.com'
port = 80
# 创建TCP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 连接指定服务端
sock.connect((url, port))
# 创建请求消息头发送的请求报文(详情见浏览器里面的请求头原文)
request_url = 'GET / HTTP/1.1\r\nHost: www.baidu.com\r\nConnection: close\r\n\r\n'
# 发送请求(send的参数为字节格式所以进行了编码)
sock.send(request_url.encode())
response = b''
# 接收返回的数据
rec = sock.recv(1024)
# 由于是TCP协议所以是分片传递,用while循环获取数据
while rec:
response += rec
rec = sock.recv(1024)
# 返回响应的文本是字节格式需要解码
print(response.decode())
由上个演示可知响应中有Content-Length存在因此就可以知到响应数据的字节长度了。
所以上述示例代码可以修改为通过判断接受报文总字节长度达标后断开连接。
自动判断响应长度示例代码
# -*- coding: utf-8 -*-
# @Time : 2023/1/8 15:56
# @Author : 红后
# @Email : not_enabled@163.com
# @blog : https://www.cnblogs.com/Red-Sun
# @File : socket_http_get_upgrade.py
# @Software: PyCharm
import socket
url = 'www.baidu.com'
port = 80
# 创建TCP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 连接指定服务端
sock.connect((url, port))
# 创建请求消息头发送的请求报文(详情见浏览器里面的请求头原文)
request_url = 'GET / HTTP/1.1\r\nHost: www.baidu.com\r\n\r\n'
# 发送请求(send的参数为字节格式所以进行了编码)
sock.send(request_url.encode())
# 接收返回的数据
rec = sock.recv(1024)
# 获取消息头与消息体分割的索引位置
index = rec.find(b'\r\n\r\n')
# 换行前为响应头报文
response_head = rec[:index]
# 索引位后移4为删除换行获取响应文本主体
response_body = rec[index+4:]
# 获取Content-Length,start_index:索引开始位,end_index:索引结束位
start_index = response_head.find(b'Content-Length')
end_index = response_head.find(b'\r\n', start_index)
content_length = int(response_head[start_index:end_index].split(b' ')[1])
# 由于是TCP协议所以是分片传递,用while循环获取数据
while len(response_body) < content_length:
rec = sock.recv(1024)
response_body += rec
# 关闭套接字
sock.close()
# 返回响应的文本是字节格式需要解码
print(response_body.decode())
POST:示例
# -*- coding: utf-8 -*-
# @Time : 2023/1/9 14:33
# @Author : 红后
# @Email : not_enabled@163.com
# @blog : https://www.cnblogs.com/Red-Sun
# @File : socket_http_post.py
# @Software: PyCharm
import socket
from urllib.parse import urlparse
url = 'http://www.baidu.com/'
# 作为一个懒癌患者加上了一个主域名解析,就不需要自己提取了,直接把目标网站扔里面结束。
host_url = urlparse(url).hostname
# http请求大部分都是80端口
port = 80
# 创建TCP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 连接指定服务端
sock.connect((host_url, port))
request_header = b'''POST / HTTP/1.1
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.9
Cache-Control: no-cache
Connection: close
Content-Length: 传递参数的长度
Content-Type: application/x-www-form-urlencoded
Host: www.baidu.cn
Origin: http://www.baidu.cn
Pragma: no-cache
Referer: http://www.baidu.cn/
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
X-Requested-With: XMLHttpRequest
'''
data = b'传递的参数(原型格式)'
# 发送请求(send的参数为字节格式所以进行了编码)
sock.send(request_header + b'\r\n' +data)
response = b''
# 接收返回的数据
rec = sock.recv(1024)
# 由于是TCP协议所以是分片传递,用while循环获取数据
while rec:
response += rec
rec = sock.recv(1024)
# 返回响应的文本是字节格式需要解码
print(response.decode())
PS:避免实例网站被搞,这里就用百度代替,此代码只是模板链接无法直接运行。
url:目标网站地址
request_header:发起的请求头(懒人做法直接去浏览器抓包后复制请求头的值就行,直接粘贴就能用),这个模板要改几个值,Connection需要是close,浏览器默认keep-alive,如果Accept-Encoding属性中有gzip最好移除,不然打印容易报错。
三、socket实现https请求
http跟https的一些区别:
端口:http默认80,https默认443
传递:HTTP协议是位于第四层协议TCP之上完成的应用层协议, 端到端都是明文传送,别人一下就能获取到数据,不太安全。HTTPS是基于SSL加密传输的,这样别人截获你的数据包破解的概率要小一点,比HTTP安全一些。
PS:个人看法,http跟https相比就差了一个ssl加密所以用ssl.wrap_socket (sock[, **opts])
包装现有套接字实现https请求。
https的GET实例
# -*- coding: utf-8 -*-
# @Time : 2023/1/9 15:34
# @Author : 红后
# @Email : not_enabled@163.com
# @blog : https://www.cnblogs.com/Red-Sun
# @File : socket_https_get.py
# @Software: PyCharm
import socket
import ssl
url = 'www.baidu.com'
port = 443
# 勇1ssl.wrap_socket封装socket
sock = ssl.wrap_socket(socket.socket(socket.AF_INET, socket.SOCK_STREAM))
# 连接指定服务端
sock.connect((url, port))
# 创建请求消息头发送的请求报文(详情见浏览器里面的请求头原文)
request_url = b'GET https://www.baidu.com/ HTTP/1.1\r\nHost: www.baidu.com\r\nConnection: close\r\n\r\n'
# 发送请求(send的参数为字节格式所以进行了编码)
sock.send(request_url)
response = b''
# 接收返回的数据
rec = sock.recv(1024)
# 由于是TCP协议所以是分片传递,用while循环获取数据
while rec:
response += rec
rec = sock.recv(1024)
# 返回响应的文本是字节格式需要解码
print(response.decode())
https的POST实现:就是对上面http的post实现就行ssl封装这里就不在做过多阐述了。
懒人直接用脚本
个人封装的脚本,主要作为学习熟练编写还有很多能封装改进的地方,暂时简化如此了,要用的话看一下注意事项
注意事项:
- url要带http或https
- post传参的data数据前面记得加个r避免\斜杠的转义效果导致报错
- 复制的request_header粘贴之后要看看有没有多余的空行有时候粘贴会自动回车
- 访问失败可以尝试删减request_header中的参数,有时候无用参数会导致这类问题
PS:暂时就想到这么多等以后遇见问题在补充吧,毕竟也只是学习笔记,慢慢学吧。
# -*- coding: utf-8 -*-
# @Time : 2023/1/9 15:37
# @Author : 红后
# @Email : not_enabled@163.com
# @blog : https://www.cnblogs.com/Red-Sun
# @File : SocketSpider.py
# @Software: PyCharm
import socket
import ssl
from urllib.parse import urlparse
class SocketSpider:
'''
socket爬虫脚本(PS:模仿requests写的仅作为学习所用)
'''
def format_socket(self, url):
'''
格式化创建socket
'''
host_url = urlparse(url).hostname
port = (80, 443)['https' == url[:5]]
if port == 80:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
else:
sock = ssl.wrap_socket(socket.socket(socket.AF_INET, socket.SOCK_STREAM))
sock.connect((host_url, port))
return sock
def format_header(self, request_header):
'''
对请求头中的数据进行处理
'''
message = b''
for header_data in request_header.split(b'\n'):
if b'gzip' in header_data:
continue
message += header_data + b'\r\n'
# message += b'\r\n'
return message.replace(b'Connection: keep-alive', b'Connection: close')
def get(self, url: str, request_header: str):
'''
get请求实现
url: 必须要带http或https
request_header: 直接从浏览器抓包复制出请求头就行
'''
sock = self.format_socket(url=url)
message = self.format_header(request_header.encode())
sock.send(message)
response = b''
# 接收返回的数据
rec = sock.recv(1024)
# 由于是TCP协议所以是分片传递,用while循环获取数据
while rec:
response += rec
rec = sock.recv(1024)
# 返回响应的文本是字节格式需要解码
return response.decode()
def post(self, url:str, request_header: str, data: str):
'''
post请求实现
url: 必须要带http或https
request_header: 直接从浏览器抓包复制出请求头就行
data: 同样直接从浏览器复制出来就行(前面最好加上r避免\斜杠的转义效果)
'''
sock = self.format_socket(url=url)
message = self.format_header(request_header.encode()) + data.encode()
sock.send(message)
response = b''
# 接收返回的数据
rec = sock.recv(1024)
# 由于是TCP协议所以是分片传递,用while循环获取数据
while rec:
response += rec
rec = sock.recv(1024)
# 返回响应的文本是字节格式需要解码
return response.decode()
if __name__ == '__main__':
url = 'https://www.baidu.com/'
request_header = '''GET / HTTP/1.1
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: zh-CN,zh;q=0.9
Cache-Control: no-cache
Connection: keep-alive
Cookie: PSTM=1664529301; BIDUPSID=5D9256DF2D0A67E0B97C22A9AC33CB09; BAIDUID=B54EAE77A79A6008061229C7A4AB5387:FG=1; MCITY=-224%3A; BD_UPN=12314753; BAIDUID_BFESS=B54EAE77A79A6008061229C7A4AB5387:FG=1; __bid_n=1845a26a63492b76484207; FEID=v10-1cec01417c6a9c7b08b333c03eb5425b40157d14; __xaf_fpstarttimer__=1672974585279; __xaf_thstime__=1672974585296; FPTOKEN=1oofVJhUqwLSMyqmyHnXJSxhD+K991pfK+BFWKI9hSqaoAEyXVZYBO320OdxlRccUo1KnxIfZK0C7Fpcc5ED9MHoxVt7PAEblzQAudnC7Di7Ic5it9yUwuAcV2iTjxygfDUcIQI5H0vbTllVkUpaX+xEyctdNFuFm0Py4wQ6EFX7bAjD0I5muVjmnRdsC/6qHg8fINmN3dEID5+n6zpiNVs+bLYePtIeO9SI/HvE7mnQxcd/HbOwf0to5q/5tcNyzO8riRarry1ryI7wocSoAnYc6HTyEaVY2xSdDYeXPNLCbMOwZvfAejftHAfvTCNRWO/K3GV0PYU/+yhEXkejECxAMOnFsbfPyhHd13AyEY0IwyNNYthFeO5qSazdfMZIVFEqenGxifjUCLsejVbcHg==|4j1jFPC8s2KJ3Bd5vfyJK+BhAEWpSgq+RbmgPQ512ro=|10|b66cd1b11d1528a08711913e6ce61baf; __xaf_fptokentimer__=1672974585330; BA_HECTOR=24ag80a4ah2la5ak05242lqk1hrnc8s1k; ZFY=gDQKa:Bq4ufkSfg5tno3jSIyvxsB0KQkWtgWw1CAvnO4:C; BDORZ=FFFB88E999055A3F8A630C64834BD6D0; COOKIE_SESSION=4_0_9_9_20_16_1_1_9_8_1_2_1125999_0_3_0_1673244971_0_1673244968%7C9%231882534_14_1670467865%7C4; BDRCVFR[t1epCe_UAtt]=OjjlczwSj8nXy4Grjf8mvqV; BD_HOME=1; delPer=0; BD_CK_SAM=1; PSINO=5; ab_sr=1.0.1_MjQ0YjU1ODM4ODAzOTE5ODQxM2U5ZTRhYjFlMDIwMzkwZGIzYjc0MjVmZmExMTZmMTA1ZmEzMmUzNGMyMGQ3ZThmNzliNmJhZGFjMDM3NDE4YzQ4NGMxMWI0YzIxY2VkMWFhNTcyMTQ1NWVlNDMyY2VkZmY5MTMwZjJmNDVhOTkwNzY1YTQwOTI4M2I2Yzg1ZTYwODBmYzhiZTgzMzA1MA==; BDRCVFR[sOxo1TgcNNt]=OjjlczwSj8nXy4Grjf8mvqV; BDRCVFR[bIKc3BPCk_C]=OjjlczwSj8nXy4Grjf8mvqV; H_PS_645EC=4e43o0N5BrE9ePh0PWMUS4TfxCmDRlE97BtwBPR4eHZmdOKlWsRxZaGgaEJYE2x9oTHCsGqhpL13; H_PS_PSSID=36555_37647_37625_36920_38035_37989_37931_26350_38009_37881
Host: www.baidu.com
Pragma: no-cache
Referer: https://cn.bing.com/
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: cross-site
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
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
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"
'''
a = SocketSpider()
a.get(url=url, request_header=request_header)
Socket爬虫:Python版的更多相关文章
- 百度图片爬虫-python版-如何爬取百度图片?
上一篇我写了如何爬取百度网盘的爬虫,在这里还是重温一下,把链接附上: http://www.cnblogs.com/huangxie/p/5473273.html 这一篇我想写写如何爬取百度图片的爬虫 ...
- 百度图片爬虫-python版
self.browser=imitate_browser.BrowserBase() self.chance=0 self.chanc ...
- 豆瓣top250(go版以及python版)
最近学习go,就找了一个例子练习[go语言爬虫]go语言爬取豆瓣电影top250,思路大概就是获取网页,然后根据页面元素,用正则表达式匹配电影名称.评分.评论人数.原文有个地方需要修改下patte ...
- Atitit 爬虫 node版 attilaxA
Atitit 爬虫 node版 attilax 1.1. 貌似不跟python压实,,java的webmagic压实,,什么爬虫框架也没有,只好自己写了. 查了百度三爷资料也没有.都是自己写.. 1. ...
- socket编程python+c
python版: server: def socket_loop_server_function(): HOST = '192.168.56.1' PORT = 21567 sk = socket.s ...
- Python版:Selenium2.0之WebDriver学习总结_实例1
Python版:Selenium2.0之WebDriver学习总结_实例1 快来加入群[python爬虫交流群](群号570070796),发现精彩内容. 实属转载:本人看的原文地址 :http:/ ...
- 【原】Learning Spark (Python版) 学习笔记(三)----工作原理、调优与Spark SQL
周末的任务是更新Learning Spark系列第三篇,以为自己写不完了,但为了改正拖延症,还是得完成给自己定的任务啊 = =.这三章主要讲Spark的运行过程(本地+集群),性能调优以及Spark ...
- 数据结构:顺序表(python版)
顺序表python版的实现(部分功能未实现) #!/usr/bin/env python # -*- coding:utf-8 -*- class SeqList(object): def __ini ...
- python版恶俗古风自动生成器.py
python版恶俗古风自动生成器.py """ python版恶俗古风自动生成器.py 模仿自: http://www.jianshu.com/p/f893291674c ...
- LAMP一键安装包(Python版)
去年有出一个python整的LAMP自动安装,不过比较傻,直接调用的yum 去安装了XXX...不过这次一样有用shell..我也想如何不调用shell 来弄一个LAMP自动安装部署啥啥的..不过尼玛 ...
随机推荐
- 微服务 Zipkin 链路追踪原理(图文详解)
一个看起来很简单的应用,可能需要数十或数百个服务来支撑,一个请求就要多次服务调用. 当请求变慢.或者不能使用时,我们是不知道是哪个后台服务引起的. 这时,我们使用 Zipkin 就能解决这个问题. 由 ...
- 教你如何解决T+0的问题
摘要:T+0查询是指实时数据查询,数据查询统计时将涉及到最新产生的数据. 本文分享自华为云社区<大数据解决方案:解决T+0问题>,作者: 小虚竹 . T+0问题 T+0查询是指实时数据查询 ...
- python(牛客)试题解析1 - 入门级
导航: 一.NC103 反转字符串 二.NC141 判断是否为回文字符串 三.NC151 最大公约数 四.NC65 斐波那契数列 - - - - - - - - - - 分-割-线 - - - - - ...
- Pictionary 方法记录
[COCI2017-2018#5] Pictionary 题面翻译 题目描述 在宇宙一个不为人知的地方,有一个星球,上面有一个国家,只有数学家居住. 在这个国家有\(n\)个数学家,有趣的是,每个数学 ...
- 【Azure 事件中心】 org.slf4j.Logger 收集 Event Hub SDK(Java) 输出日志并以文件形式保存
问题描述 在使用Azure Event Hub的SDK时候,常规情况下,发现示例代码中并没有SDK内部的日志输出.因为在Java项目中,没有添加 SLF4J 依赖,已致于在启动时候有如下提示: SLF ...
- ElasticSearch深度分页详解
1 前言 ElasticSearch是一个实时的分布式搜索与分析引擎,常用于大量非结构化数据的存储和快速检索场景,具有很强的扩展性.纵使其有诸多优点,在搜索领域远超关系型数据库,但依然存在与关系型数据 ...
- ANSYS安装教程
ANSYS 16.0 WIN10 64位安装步骤:1.使用"百度网盘客户端"下载ANSYS 16.0软件安装包到电脑磁盘里全英文名称文件夹内,安装前先断开网络,然后找到ANSYS. ...
- 一个jsqlparse+git做的小工具帮我节省时间摸鱼
背景 前些时间做了个小工具解决了团队内数据库脚本检验&多测试环境自动执行的问题,感觉挺有意思,在这跟大家分享一下. 工具诞生之前的流程是这样: 1.开发人员先在开发环境编写脚本&执行: ...
- postgresql函数:定期删除模式下指定天数前的表数据及分区物理表
一.现有函数-- 1.现有函数调用select ods.deletePartitionIfExists('fact_ship' || '_' || to_char(CURRENT_DATE - INT ...
- 【SQL基础】【关键字大写】条件查询:比较、不等于、IN、为空、BETWEEN
〇.概述 1.内容介绍 条件查询:比较.不等于.IN.为空.BETWEEN 2.建表语句 drop table if exists user_profile; CREATE TABLE `user_p ...