爬虫心得
爬虫的最终目的是通过程序模拟用户上网,爬取目标网站的相关数据。那么,就需要有python数据结构的相关知识,主要涉及序列、元组和字典。
-
序列
序列是Python中最基本的数据结构。序列中的每个元素都分配一个数字 - 它的位置,或索引,第一个索引是0,第二个索引是1,依此类推。
list1 = ['physics', 'chemistry', 1997, 2000]
-
元组
tup1 = ('physics', 'chemistry', 1997, 2000)
-
字典
dict = {'Name': 'Zara', 'Age': 7, 'Class': 'First'} #访问字典里的值 dict['Name'] #输出字典可打印的字符串表示 str(dict)
GET请求目标网站
-
实现步骤:
第一步:获取目标网站的url
第二步:通过requests库发送get请求
第三步:设置参数用来接收返回的网站
第四步:通过xpath解析数据
-
案例分析
import requests from lxml import etree url="http://www.sxjybk.com/gdjy.htm" headers={ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36', } resp=requests.get(url,headers=headers); resp.encoding=resp.apparent_encoding html=etree.HTML(resp.text); #获取总页数 _pageNum=html.xpath("/html/body/div[3]/div[3]/div/span/span[9]/a/text()") pageNum="".join(_pageNum);#数组转字符串 #print(pageNum) currentPage=int(pageNum) while currentPage>0: page_url = "http://www.sxjybk.com/gdjy/"+str(currentPage)+".htm" #print(page_url) #currentPage=currentPage-1 _resp = requests.get(page_url, headers=headers); _resp.encoding = _resp.apparent_encoding html = etree.HTML(_resp.text); a_list=" ".join(html.xpath("/html/body/div[3]/div[3]/ul/li/a[2]/@href")) #print(a_list) news_list=a_list.replace("..","http://www.sxjybk.com/").split() #print(news_list) for href in news_list: #print(href) resp = requests.get(href, headers=headers); resp.encoding = resp.apparent_encoding item_html = etree.HTML(resp.text) #print(item_html) content = "".join(item_html.xpath("//html/body/div[3]/form/div[3]/div[1]/div/p/text()")) title = "".join(item_html.xpath("/html/body/div[3]/form/div[1]/span[2]/text()")) source = "".join(item_html.xpath("/html/body/div[3]/form/div[1]/div[1]/span[1]/text()")).strip().replace("来源:"," ") date_time = "".join(item_html.xpath("/html/body/div[3]/form/div[1]/div[1]/span[3]/text()")).replace('年', '-').replace('月', '-').replace('日', '') #print(date_time) if content!="": edu_info = { "title": title, "date": date_time, "source": source, "content": content } else: content_02="".join(item_html.xpath("/html/body/div[3]/form/div[3]/div[1]/div/div/p/text()")) edu_info = { "title": title, "date": date_time, "source": source, "content": content_02 } print(edu_info) currentPage=currentPage-1 #print(page_url) resp.close();
POST请求目标网站
如果通过xpath解析,返回的是空列表,然后通过谷歌浏览器开发者模式,在response的源代码中搜索目标内容,如果没搜到,并且再次发送请求网站没刷新,很大程度上,该页面是通过ajax发出异步请求将返回的数据渲染在页面上的。
-
实现步骤
第一步:获取目标网站的url
第二步:通过requests库发送get请求
第三步:设置参数用来接收返回的网站
第四步:通过xpath解析数据
-
案例分析:
import requests from lxml import etree import re url = "https://www.shxbe.com/front/node/4/nodeData" params = { 'nodeId': 4, 'pageSize': 9, 'curPage': 1 } headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36', } resp = requests.post(url, headers=headers, data=params); resp.encoding = resp.apparent_encoding html = etree.HTML(resp.text); if resp.status_code == 200: info = resp.json() # 解析内容为json返回 current_page = int(info["curPage"]) pageNum = int(info['totalPage']) while current_page <= pageNum: page_url = "https://www.shxbe.com/front/node/4/nodeData" params = { 'nodeId': 4, 'pageSize': 9, 'curPage': current_page } _resp = requests.post(page_url, headers=headers, data=params); _resp.encoding = _resp.apparent_encoding html = etree.HTML(_resp.text); if _resp.status_code == 200: info = _resp.json() # 解析内容为json返回 id_list = info["list"] for value in id_list: href = "https://www.shxbe.com/front/node/recordInfor/" + str(value['id']) resp = requests.get(href, headers=headers); resp.encoding = resp.apparent_encoding item_html = etree.HTML(resp.text) title = "".join(item_html.xpath("/html/body/div[2]/div[2]/h1/text()")) source = "".join(item_html.xpath("/html/body/div[2]/div[2]/div[3]/p/i[1]/text()")) date = "".join(item_html.xpath("/html/body/div[2]/div[2]/div[1]/div[1]/i/text()")) _content = "".join(item_html.xpath("/html/body/div[2]/div[2]/div[2]/p/span/text()")) content = re.sub("\s", '', _content); if content != "": edu_info = { 'title': title, 'source': source, 'date': date, 'content': content } print(edu_info) current_page = current_page + 1 resp.close()
几个重要的方法
-
字符串相关
方法 说明 “”.join(param1) 将param1转字符串 str(obj) 返回一个对象的string格式 strip() 去除首尾空格 -
re相关
-
正则表达式
量词 控制前面的元字符出现的次数
量词 说明 * 重复零次或多次 + 重复1次或多次 ? 重复0次或一次 {n} 重复n次 .* 贪婪匹配 .*? 惰性匹配 常用元字符
模式 描述 ^ 匹配字符串的开头 $ 匹配字符串的末尾。 . 匹配换行符以外的任意字符 […] 用来表示一组字符,单独列出:[amk] 匹配 ‘a’,‘m’或’k’ [^…] 不在[]中的字符 \w 匹配字母、数字、下划线 \s 匹配任意空白字符,等价于 [ \t\n\r\f]。 \d 匹配任意数字,等价于 [0-9] -
方法
引入re库:import re
方法名 说明 举例 sub(pattern, repl, string, count=0, flags=0) 替换字符串中的匹配项 content = re.sub("\s", ‘’, _content);替换空白字符 findall(string[, pos[, endpos]]) 返回正则表达式所匹配的所有子串 列表 source = re.findall(r"文章来源:(.+?) ", _source)
-
tips
一、伪装成正常访问 审查元素 Networks--Headers---User-Agent
headers={User-Agent:" "}
requests.get(url,headers=headers)
二、爬取最后一步 resp.close()
三、数据解析
(1)re解析 正则表达式 re模块
(2)bs4解析
(3)xpath解析
安装lxml pip install lxml
匹配规则:
/表示层级关系,第一个/是根节点
text()取标签内容
//后代
*任意的节点,通配符
页面复杂时,结合浏览器审查元素 copy xpath路径
四、请求参数过长,重新封装参数
Network-->Headers-->Query String Parameters (保存在params)
url="https://Www.baidu.com"
params={
"name":"pinkhub"
}
等价于url="https://www.baidu.com/?name=pinkhub"
安装requests插件 pip install requests
换源加速 pip install -i https://pypi.tuna.tsinghua.edu.cn/simple some-package
五、注意点
1.如果xpath路径中存在tbody,要删掉tbody
2.发出请求没返回内容
(1)UA伪装
(2)反爬机制
3.乱码问题 windows系统电脑默认是gbk,需添加encoding='utf-8'
4.robots.txt协议 规范爬虫 网址/robots.txt