爬虫系列(一)

版权声明:本文为博主原创文章,欢迎阅读转载,和引用。 https://blog.csdn.net/Moluth/article/details/82527931

爬虫系列(一)

又是很久没有写文章了,这段时间工作中积累了很多。实践了不少网站的爬取,不写下来怕烂在脑子里,最终什么也不剩。爬虫这个分类已经建立很久了,里面文章数量始终是0,今天决定打破这0的寂静。发现新版本的csdn编辑器不会用了,尴尬。。。以后文章只好换风格了。

开始对爬虫了解是从毕业设计开始的,毕设写的是一个基于C语言TCP编程的web服务器。 功能类似于Tomcat,Nginx等,不过功能比起这些low到爆。写出来后欣喜了好久,现在看来真是low到爆。写的过程中,对http协议有了大致的了解,了解后自然而然的明白了爬虫是什么,应该怎么玩。还想过自己写一个web浏览器,解析html,解析js,显示……只是想想,是个浩大的工程,还是算了吧。

本系列文章都是基于python3的。如果你是一个使用其他语言的程序员也是可以阅读的,毕竟原理都是一样的。当年不知道什么是爬虫,听名字觉得好牛逼。真正搞了后,发现这玩意儿也就这样了。没有想象中的复杂,甚至会觉得有点简单。


1.从TCP客户端和服务器端开始

TCP协议可以实现两个有ip地址的机器上的两个进程相互发消息,就像聊天一样。

服务器端一直检查要监听的端口是否有消息,当收到消息后处理消息。

客户端主动向服务器端某端口发送消息。

下面是服务器端程序,里面有注释:

import socket

ip_port=("127.0.0.1",8001)#定义监听端口
skt=socket.socket()#创建socket对象,默认是TCP协议
skt.bind(ip_port)#绑定监听端口
skt.listen()#开始监听绑定的端口
while True:
    #阻塞等待客户端向8001端口发送的消息
    connect,addr=skt.accept()
    #获取客户端发送的数据
    client_data=connect.recv(1024)
    #输出客户端消息
    print("收到客户端消息:")
    print(addr)
    print(client_data)
    print("#"*40)
    print(str(client_data,"utf-8"))
    print("#"*40)
    
    #给客户端发送消息
    connect.sendall("你好,消息来自服务器端.".encode(encoding='utf_8'))
    #关闭连接
    connect.close()

下面是客户端程序:

import socket

#设置连接超时时间
socket.setdefaulttimeout(3)
#创建socket对象
tcpsocket=socket.socket()
#请求服务器端建立连接
tcpsocket.connect(("127.0.0.1",8001))
#给服务器端发送消息
tcpsocket.send("服务器你好。".encode(encoding='utf_8'))
#接收服务器端消息
msg=tcpsocket.recv(1024)
#输出消息
print(msg)
print(str(msg,"utf-8"))
#关闭
tcpsocket.close()

 下面是运行,及结果:

#启动服务器端
#启动客户端
#服务器端输出:

收到客户端消息:
('127.0.0.1', 51057)
b'\xe6\x9c\x8d\xe5\x8a\xa1\xe5\x99\xa8\xe4\xbd\xa0\xe5\xa5\xbd\xe3\x80\x82'
########################################
服务器你好。
########################################

#客户端输出:

b'\xe4\xbd\xa0\xe5\xa5\xbd\xef\xbc\x8c\xe6\xb6\x88\xe6\x81\xaf\xe6\x9d\xa5\xe8\x87\xaa\xe6\x9c\x8d\xe5\x8a\xa1\xe5\x99\xa8\xe7\xab\xaf.'
你好,消息来自服务器端.


这时客户端已经结束运行了,服务器端继续监听8001端口。


2.有趣的尝试

如果用谷歌浏览器访问一下刚才的服务器端会有什么好玩的结果呢,试一下吧。

浏览器上显示错误,好像这个尝试没有什么意思。

控制台输出:

########################################
收到客户端消息:
('127.0.0.1', 51137)
b'GET / HTTP/1.1\r\nHost: 127.0.0.1:8001\r\nConnection: keep-alive\r\nCache-Control: max-age=0\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: zh-CN,zh;q=0.9,en;q=0.8\r\n\r\n'
########################################
GET / HTTP/1.1
Host: 127.0.0.1:8001
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8

好像发现了什么。浏览器发送过来了有一定格式的浏览器的信息,如果按照这样的格式写一个客户端程序请求百度会有什么效果???

先看看数据格式吧

数据1\r\n数据2\r\n...数据n\r\n\r\n


3.说干就干,按照上面的格式请求百度,想想就很有趣。对上面浏览器的数据进行一些修改

疑问:百度服务器端ip地址是多少?打开命令行输入:ping www.baidu.com 可以看到ip是61.135.169.121。这里可以不需要ip,http服务默认端口是80。

下面是请求百度客户端的源码:

import socket

#设置连接超时时间
socket.setdefaulttimeout(3)
#创建socket对象
tcpsocket=socket.socket()
#请求服务器端建立连接
tcpsocket.connect(("www.baidu.com",80))
#给服务器端发送消息
tcpsocket.send(b'GET / HTTP/1.1\r\nHost: www.baidu.com\r\nConnection: keep-alive\r\nCache-Control: max-age=0\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: zh-CN,zh;q=0.9,en;q=0.8\r\n\r\n')
#接收服务器端消息
msg=tcpsocket.recv(1024)
#输出消息
print(msg)
print(str(msg,"utf-8"))
#关闭
tcpsocket.close()

下面是运行结果:

b'HTTP/1.1 302 Found\r\nConnection: Keep-Alive\r\nContent-Length: 225\r\nContent-Type: text/html\r\nDate: Sat, 08 Sep 2018 06:54:50 GMT\r\nLocation: https://www.baidu.com/\r\nP3p: CP=" OTI DSP COR IVA OUR IND COM "\r\nServer: BWS/1.1\r\nSet-Cookie: BAIDUID=70645756D952283CCEC6AE82F1E1EA58:FG=1; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com\r\nSet-Cookie: BIDUPSID=70645756D952283CCEC6AE82F1E1EA58; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com\r\nSet-Cookie: PSTM=1536389690; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com\r\nSet-Cookie: BD_LAST_QID=18430702357974489400; path=/; Max-Age=1\r\nX-Ua-Compatible: IE=Edge,chrome=1\r\n\r\n<html>\r\n<head><title>302 Found</title></head>\r\n<body bgcolor="white">\r\n<center><h1>302 Found</h1></center>\r\n<hr><center>8ad17104306abbd024edb33771d4064e4479fd30\nTime : Wed Aug 29 14:48:25 CST 2018</center>\r\n</body>\r\n</html>\r\n'
HTTP/1.1 302 Found
Connection: Keep-Alive
Content-Length: 225
Content-Type: text/html
Date: Sat, 08 Sep 2018 06:54:50 GMT
Location: https://www.baidu.com/
P3p: CP=" OTI DSP COR IVA OUR IND COM "
Server: BWS/1.1
Set-Cookie: BAIDUID=70645756D952283CCEC6AE82F1E1EA58:FG=1; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie: BIDUPSID=70645756D952283CCEC6AE82F1E1EA58; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie: PSTM=1536389690; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com
Set-Cookie: BD_LAST_QID=18430702357974489400; path=/; Max-Age=1
X-Ua-Compatible: IE=Edge,chrome=1

<html>
<head><title>302 Found</title></head>
<body bgcolor="white">
<center><h1>302 Found</h1></center>
<hr><center>8ad17104306abbd024edb33771d4064e4479fd30
Time : Wed Aug 29 14:48:25 CST 2018</center>
</body>
</html>

百度会返回一个重定向信息:Location: https://www.baidu.com/ 

想让我们请求这个地址https://www.baidu.com/ ,基于TCP需要我们手动实现https协议。如果是一个普通的http网站,就可以看到返回正常的html了。实现起来很麻烦,很多编程语言的一些库中已经实现了这个,我们直接使用。


4.下载一个网页

#coding=utf-8
#网页爬虫

from urllib import request
header_dict = {
    "Accept":"application/json, text/javascript, */*; q=0.01",
    "Accept-Language":"zh-CN,zh;q=0.9",
    "User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36",
    }
def get_http(load_url,header):
    res=""
    try:
        req = request.Request(url=load_url,headers=header)#创建请求对象
        coonect = request.urlopen(req)#打开该请求
        byte_res = coonect.read()#读取所有数据,很暴力
        try:
            res=byte_res.decode(encoding='utf-8')
        except:
            res=byte_res.decode(encoding='gbk')
    except Exception as e:
        print(e)
    return res

page=get_http("https://www.baidu.com", header_dict)
print(page)

运行结果太长,直接截图:


上面4中,已经是一个简单的爬虫了,功能是爬取某连接的html。实际任务中,可以是要爬某网站的某些网页,或者全部网页。也有可能要爬取图片,音频,视频。也有可能是网页固定区域的文本。可以看到,上面的代码不能完成这些任务的。欲知后事,请看下篇。

猜你喜欢

转载自blog.csdn.net/Moluth/article/details/82527931