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自动安装部署啥啥的..不过尼玛 ...
随机推荐
- Linux系统管理_网络管理
常用网络指令 yum -y install net-tools #安装ifconfig工具 ifconfig #查看网络配置 ifup ens33 #启用网卡 ifdown ens33 #禁用网卡 s ...
- C语言------数据类型与输入输出
仅供借鉴.仅供借鉴.仅供借鉴(整理了一下大一C语言每个章节的练习题.没得题目.只有程序了) 文章目录 1 .实训名称 2 .实训目的及要求 3 .源代码及运行截图 4 .小结 1 .实训名称 实训2: ...
- java.lang.ClassNotFoundException: org.springframework.web.filter.CharacterEncodingFilter 增样将jar包导入
2021-9-30-17:28 遇到的一个bug.以前遇到过,这次又遇到.就离谱,结果还忘记怎样解决了.这捣鼓一下,那捣鼓一下,又给搞好了.为了记录这次bug,又试图还原bug. 1.解决办法file ...
- java中获取当前执行线程的名称
Thread.currentThread().getName()
- pip cmd下载速度慢解决方案
cmd下载速度慢不是电脑问题,而是下载的网站有网速限制,如pip,虽然没被墙,但由于是外网,网速极差,经常是几KB一秒,所以我们可以采用镜像服务器,即在命令后加上 -i https://pypi.tu ...
- python的微积分运算
import sympy sympy.init_printing() from sympy import I, pi, oo import numpy as np 求函数的导数 x = sympy.S ...
- Linux网络管理命令
Linux网络管理命令 ifconfig 用于配置网卡ip地址信息等网络参数或显示网络接口状态,类似于windows的ipconfig命令. 可以用这个工具来临时性的配置网卡的IP地址.掩码.广播地址 ...
- python3使用libpcap库进行抓包及数据处理
python版本:python 3.9 libpcap版本:1.11.0b7 python libpcap库是底层绑定c语言libpcap库的开发包,旨在提供python应用可访问的unix c li ...
- Sprint产品待办列表的优先级要怎么排?
在梳理产品待办事项列表的过程中,产品负责人需要先做优先级排列,保证我们 在一定的时间盒内能够交付需要优先级最高.最具价值的用户故事. 那这个用户故事的优先级要怎么排列,我们怎样选择用户故事的实现顺序? ...
- 2022-11-10 Acwing每日一题
本系列所有题目均为Acwing课的内容,发表博客既是为了学习总结,加深自己的印象,同时也是为了以后回过头来看时,不会感叹虚度光阴罢了,因此如果出现错误,欢迎大家能够指出错误,我会认真改正的.同时也希望 ...