======简答的爬虫===========
简单的说,爬虫的意思就是根据url访问请求,然后对返回的数据进行提取,获取对自己有用的信息。然后我们可以将这些有用的信息保存到数据库或者保存到文件中。如果我们手工一个一个访问提取非常慢,所以我们需要编写程序去获取有用的信息,这也就是爬虫的作用。
一、概念:
网络爬虫,也叫网络蜘蛛(Web Spider),如果把互联网比喻成一个蜘蛛网,Spider就是一只在网上爬来爬去的蜘蛛。网络爬虫就是根据网页的地址来寻找网页的,也就是URL。举一个简单的例子,我们在浏览器的地址栏中输入的字符串就是URL,例如:https://www.baidu.com/
URL就是同意资源定位符(Uniform Resource Locator),它的一般格式如下(带方括号[]的为可选项):
protocol :// hostname[:port] / path / [;parameters][?query]#fragment
URL的格式由三部分组成:
(1)protocol:第一部分就是协议,例如百度使用的就是https协议;
(2)hostname[:port]:第二部分就是主机名(还有端口号为可选参数),一般网站默认的端口号为80,例如百度的主机名就是www.baidu.com,这个就是服务器的地址;
(3)path:第三部分就是主机资源的具体地址,如目录和文件名等。
网络爬虫就是根据这个URL来获取网页信息的。
二、简单爬虫实例
在Python3.x中,我们可以使用urlib这个组件抓取网页,urllib是一个URL处理包,这个包中集合了一些处理URL的模块,如下:
1.urllib.request模块是用来打开和读取URLs的;
2.urllib.error模块包含一些有urllib.request产生的错误,可以使用try进行捕捉处理;
3.urllib.parse模块包含了一些解析URLs的方法;
4.urllib.robotparser模块用来解析robots.txt文本文件.它提供了一个单独的RobotFileParser类,通过该类提供的can_fetch()方法测试爬虫是否可以下载一个页面。
我们使用urllib.request.urlopen()这个接口函数就可以很轻松的打开一个网站,读取并打印信息。
urlopen有一些可选参数,具体信息可以查阅Python自带的documentation。
了解到这些,我们就可以写一个最简单的程序:
# 爬虫项目 from urllib import request if __name__ == "__main__": response = request.urlopen("http://qiaoliqiang.cn") html = response.read() html = html.decode("utf-8") print(html)
结果:
E:pythonWorkSpaceFirstProjectvenvScriptspython.exe E:/pythonWorkSpace/FirstProject/HelloPython/reptile.py <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8">
........
上述代码有一个缺陷就是我们需要知道网站的编码格式才能正确的解析,所以我们需要改进
三、自动获取网页编码方式的方法
获取网页编码的方式有很多,个人更喜欢用第三方库的方式。
首先我们需要安装第三方库chardet,它是用来判断编码的模块,安装方法如下图所示,只需要输入指令:(或者再pycharm中的File->Settings->Project Inceptor中点击+号搜索chardet)
pip install chardet
安装好后,我们就可以使用chardet.detect()方法,判断网页的编码方式了。至此,我们就可以编写一个小程序判断网页的编码方式了。
# 爬虫项目2(自动获取)
from urllib import request
import chardet
if __name__ == "__main__":
response = request.urlopen("http://qiaoliqiang.cn/")
html = response.read()
charset = chardet.detect(html)#返回的是一个字典
print(charset)#{'encoding': 'utf-8', 'confidence': 0.99, 'language': ''}
html = html.decode(charset["encoding"])
print(html)
结果:
{'encoding': 'utf-8', 'confidence': 0.99, 'language': ''} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>XXXXXXXXXXX</title> <script src="JS/jquery-1.8.3.js"></script>
...
至此实现了简单的爬虫,接下来就是提取返回的信息。也就是解析获取到的数据。
========urllib发送get请求和post请求=============
首先搭建一个后台服务器,SpringBoot项目搭建的一个小项目,在filter中获取请求的方式以及参数:
package cn.qs.filter; import java.io.IOException; import java.util.Enumeration; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import cn.qs.bean.user.User; /** * 登陆过滤器 * * @author Administrator * */ @WebFilter(filterName = "loginFilter", urlPatterns = "/*") public class LoginFilter implements Filter { private static final Logger logger = LoggerFactory.getLogger(LoginFilter.class); public LoginFilter() { } public void destroy() { } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; String method = req.getMethod(); System.out.println("请求方式: " + method); Enumeration<String> parameterNames = request.getParameterNames(); while (parameterNames.hasMoreElements()) { String key = (String) parameterNames.nextElement(); System.out.println(key + " " + request.getParameter(key)); } response.setContentType("text/html;charset=UTF-8"); response.getWriter().write("回传中文"); } public void init(FilterConfig fConfig) throws ServletException { } }
(1)发送一个不携带参数的get请求
from urllib import request if __name__ == "__main__": response = request.urlopen("http://localhost:8088/login.html") html = response.read() #解码 html = html.decode('utf-8') print(html)
结果:
回传中文
JavaWeb控制台打印信息如下:
请求方式: GET
(2)发送一个携带参数的get请求
import urllib.request import urllib.parse # 定义出基础网址 base_url='http://localhost:8088/login.html' #构造一个字典参数 data_dict={ "username":"张三", "password":"13221321", "utype":"1", "vcode":"2132312" } # 使用urlencode这个方法将字典序列化成字符串,最后和基础网址进行拼接 data_string=urllib.parse.urlencode(data_dict) print(data_string) new_url=base_url+"?"+data_string response=urllib.request.urlopen(new_url) print(response.read().decode('utf-8'))
结果:
password=13221321&utype=1&vcode=2132312&username=%E5%BC%A0%E4%B8%89
回传中文
JavaWeb控制台打印信息如下:
请求方式: GET
password 13221321
utype 1
vcode 2132312
username 张三
(3)携带参数的POST请求
import urllib.request import urllib.parse #定义一个字典参数 data_dict={"username":"张三","password":"123456"} #使用urlencode将字典参数序列化成字符串 data_string=urllib.parse.urlencode(data_dict) #将序列化后的字符串转换成二进制数据,因为post请求携带的是二进制参数 last_data=bytes(data_string,encoding='utf-8') print(last_data) #如果给urlopen这个函数传递了data这个参数,那么它的请求方式则不是get请求,而是post请求 response=urllib.request.urlopen("http://localhost:8088/login.html",data=last_data) #我们的参数出现在form表单中,这表明是模拟了表单的提交方式,以post方式传输数据 print(response.read().decode('utf-8'))
结果:
b'password=123456&username=%E5%BC%A0%E4%B8%89' 回传中文
JavaWeb控制台打印信息如下:
请求方式: POST
password 123456
username 张三
补充:一个例子,python读取数据库,并读取url、method、param去访问请求,最后将结果记录输出到html中:
#!/usr/bin/python3 import pymysql from urllib import request import urllib.parse import chardet import json # 访问请求的方法 def requestUrl(result): url = str(result['url']); method = str(result['method']); data = str(result['param']); if url is None or method is None: return; if data is not None: data = str(data); data = data.replace("form=" , ""); # 去掉form= #数组参数处理 if data.startswith('[') and data.endswith(']'): datas = json.loads(data); if len(datas) > 0: data = json.dumps(datas[0]) else : data = '{"time": 1}'; elif "{}" == data or "" == data: data = '{"time": 1}'; else: data = '{"time": 1}'; try: # POST请求 if 'POST' in method: # 将序列化后的字符串转换成二进制数据,因为post请求携带的是二进制参数 last_data = bytes(data, encoding='utf-8'); response = urllib.request.urlopen(url, data=last_data); responseResult = response.read().decode('utf-8') result['responseResult'] = responseResult else: data_string=urllib.parse.urlencode(data); new_url = url + "?" + data_string; response=urllib.request.urlopen(new_url) responseResult = response.read().decode('utf-8') result['responseResult'] = responseResult except Exception as e: result['responseResult'] = "error,原因: " + str(e) # 输出爬取到的数据到本地磁盘中 def out_html(datas): if datas is None: return; file = open('D:\out.html', 'w', encoding='utf-8') file.write("<html>") file.write(r''' <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> '''); file.write("<head>") file.write("<title>爬取结果</title>") # 设置表格显示边框 file.write(r''' <style> table{100%;table-layout: fixed;word-break: break-all; word-wrap: break-word;} table td{border:1px solid black;300px} </style> ''') file.write("</head>") file.write("<body>") file.write("<table cellpadding='0' cellspacing='0'>") # 遍历datas填充到表格中 for data in datas: file.write("<tr>") file.write("<td>%s</td>" % data['interfaceName']) file.write('<td><a href='+str(data['url'])+'>'+str(data['url'])+'</a></td>') file.write("<td>%s</td>" % data['method']) file.write("<td>%s</td>" % data['param']) file.write("<td>%s</td>" % data['responseResult']) file.write("</tr>") file.write("</table>") file.write("</body>") file.write("</html>") #主函数用法 if __name__ == '__main__': # 打开数据库连接 db = pymysql.connect("localhost", "root", "123456", "pycraw") # 使用cursor()方法获取操作游标 cursor = db.cursor(cursor = pymysql.cursors.DictCursor) # SQL 查询语句 sql = "SELECT * FROM interface "; try: # 执行SQL语句 cursor.execute(sql) # 获取所有记录列表 results = cursor.fetchall() for result in results: requestUrl(result); out_html(results); print("处理完成") except Exception as e: print(e); # 关闭数据库连接 db.close()
结果: