自定义opener
- from urllib.request import HTTPHandler,Request,build_opener
- 1.http_handler=HTTPHandler() 创建HTTPHandler实例对象
- 2.opener=build_opener(http_handler)创建支持处理HTTP请求的opener对象
- 3.url=’www.baidu.com’
- 4.request=Request(url)
- 5.response=opener.open(request)
ProxyHandler
- proxy_hander = ProxyHandler({“http”:”114.67.228.126:16819”})
- opener = build_opener(proxy_hander)
获取Cookie,并保存到CookieJar()对象中
- from urllib.request import HTTPCookieProcessor,build_opener
- from http.cookiejar import CookieJar
- 1.cookiejar = CookieJar()构建一个cookiejar对象实例存储cookiejar
- 2.handler = HTTPCookieProcessor(cookiejar=cookiejar) # 使用HTTPCookieProcessor()来创建cookie处理器对象
- 3.opener = build_opener(handler)
- 4.response = opener.open(“http://www.baidu.com/“)
- 5.打印出cookie:
cookie_str = "" for item in cookiejar: # print(item) cookie_str = cookie_str +item.name+"="+item.value+";" cookie_str = cookie_str[:-1]#把最后一个分号干掉 print(cookie_str)
利用cookie登录
- 利用浏览器登录抓取cookie
- 使用sublime处理handler:
- 替换:正则---findwhat: ^(.*): (.*)$ Replacewhat: "\1":"\2",
- from urllib.request import Request,urlopen
- headers={填入处理好的header信息}
- request=Request(url,headers=headers)
- response=urlopen(request)
import request response = requests.get("http://www.baidu.com/",params=,headers=) print(response.request) #打印出是什么类型的请求 print(response.content) #打印出返回的二进制内容 print(response.text) #打印解码后的数据
保存图片
with open('name.jpg','wb') as f: for block in response.iter_content(1024): if not block: break f.write(response.content)
match
content=’hello world python’
pattern=re.compile(r’python’)
print(pattern.match(content)
search/findall
pattern=re.compile(r’\d’)
content=’qwer1234’
print(pattern.search(content)
得到一个,findall得到全部
爬取内涵段子
#需求,爬取内涵吧http://www.neihan8.com/article/list_5_1.html的段子 import requests import re class Spider(object): #第一步,请求页面的数据 #page就是爬取的页数 def loader_page(self,page): url = "http://www.neihan8.com/article/list_5_"+str(page)+".html" response = requests.get(url) return response.content def write_file(self,item): with open("内涵吧段子.txt","a") as f: f.write(item) if __name__ == "__main__": print(""" 内涵吧小爬虫开始干活了 """) page = 1 spider = Spider() swicth = True while swicth: cmd = input("请按回车键,爬虫开干,输入quit退出爬取:") if cmd == "quit": swicth = False print("当前正在爬取[%d]页面" % page) content = spider.loader_page(page) content = content.decode("gbk") # print(content) #第二步,使用正则得到段子数据,数据清洗 pattern = re.compile(r'
(.*?)',re.S) lists = pattern.findall(content) for item in lists: item = item.replace("","").replace("
","").replace("
","").replace("“","").replace("…","").replace("”","") print(item) spider.write_file(item) page += 1
XML和HTML
- XML:可扩展标记语言,被设计为传输和存储数据,其焦点是数据的内容。
- HTML:超文本标记语言,显示数据以及如何更好显示数据。
XML节点
- 父 (Parent) :每个元素以及属性都有一个父。
- 子 (Children) :元素节点可有零个、一个或多个子。
- 同胞(Sibling):拥有相同的父的节点
- 先辈(Ancestor):某节点的父、父的父,等等
XPath定义
XPath (XML Path Language) 是一门在 XML 文档中查找信息的语言,可用来在 XML 文档中对元素和属性进行遍历。
XPath语法
- nodename 选取此节点的所有子节点。
- / 从根节点选取。
- // 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。(从任何地方开始不用使用/根节点一级一级往下找)
- . 选取当前节点。
- .. 选取当前节点的父节点。
- @ 选取属性。要加上中括号
- //@name 选取所有为name的属性
- 选取未知节点:
- * 匹配任何元素节点。 - @*匹配任何属性节点。需要结合其他使用。 - node()匹配任何类型的节点。
- 运算符
- 选择多个使用 | 连接 例.11
- 加+ 减- 乘* 除div—->返回计算结果
- 等于= 不等于!= 小于< 小于等于<= 或or 与and —>返回值是true和false
- mod 计算出发的余数
- 得到文本信息: //name/text()
- 实例应用:1. /bookstore/book[1] 选取属于 bookstore 子元素的第一个 book 元素。 2. /bookstore/book[last()] 选取属于 bookstore 子元素的最后一个 book 元素。 3. /bookstore/book[last()-1] 选取属于 bookstore 子元素的倒数第二个 book 元素。 4. /bookstore/book[position()<3] 选取最前面的两个属于 bookstore 元素的子元素的 book 元素。 5. //title[@lang] 选取所有拥有名为 lang 的属性的 title 元素。 6. /bookstore/book[price>35.00] 选取 bookstore 元素的所有 book 元素,且其中的 price 元素的值须大于 35.00。 7. /bookstore/book[price>35.00]/title 选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35.00。 8. /bookstore/* 选取 bookstore 元素的所有子元素。 9. //* 选取文档中的所有元素。 10. //title[@*] 选取所有带有属性的 title 元素。 11. //book/title | //book/price 选取 book 元素的所有 title 和 price 元素。
四大对象种类:
- Tag
- suop.p – 得到第一个p标签
- suop.p.text/string – 获取p的内容
- suop.p.attrs – 获取p的属性(字典) p.attrs[“class”] – 获取class属性的值
- suop.p[“class”] = “new” – 修改属性值
NavigableString
BeautifulSoup
Comment 是NavigableString的子类
遍历文档树
- 直接子节点: .content/.children
- .content 属性可以将tag的子节点以列表的方式输出,可以使用[1]/[2]方式取出
- .children 以list生成器方式输出,可以使用for循环得到内容
- 所有后代节点: .descendants
- 遍历获取其中的内容。for child in soup.descendants
搜索文档树
- find_all
- name 参数可以查找所有名字为 name 的tag,字符串对象会被自动忽略掉
- 参数为正则 find_all(re.compile(‘a’)
- 参数为列表 find_all([“a”, “b”])
- keyword 参数 : 查找id find_all(id=’idname’)
- text 参数 : 通过 text 参数可以搜搜文档中的字符串内容,与 name 参数的可选值一样, text 参数接受 字符串 , 正则表达式
- href : links = soup.find_all(href=re.compile(r’http://example.com/‘))
- 选择器(select) : soup.select(),返回类型是 list
- 直接通过标签名查找 : soup.select(‘title’)
- 通过类名查找 : soup.select(‘.sister’)
- 通过id查找 : soup.select(‘#link1’)
- 组合查找 p下的id=link1 : soup.select(‘p #link1’)–直接子标签查找,则使用 > 分隔:soup.select(“head > title”)
- 属性查找 : soup.select(‘a[class=”sister”]’)
- 获取内容: get_text() — soup.select(‘title’)[0].get_text()
json模块
- json.loads()–Json转Python对象,在内存
- dict_str=json.loads(“{‘name’:’zhangsan’}”)
- json.dumps()–python转json字符串,在内存
- json.dump()–Python转json对象,写入文件
- list_str = [{“city”: “北京”}, {“name”: “大刘”}]
- fw = open(“list_str.json”,”w”,encoding=”utf-8”)
- json.dump(list_str, fw, ensure_ascii=False)
- json.load()–json转python类型,读取文件
JsonPath
- 语法:
- 根节点: $
- 当前节点: @
- 子节点: .or[]
- 就是不管位置,选择所有符合条件的条件 .. 相当于xpath的//
- 匹配所有元素节点 *
多线程保存文件需要使用线程同步保证数据的安全 —互斥锁
page_queue.get(block=False) # 取出队列中的数据 参数block=False 设置为非阻塞,队列为空后再取会报错
创建线程:
crawl.start()
class ThreadParse(Thread):
def init(self,参数):
super(ThreadParse,self).init()
self.参数=参数
def run(self):
线程执行的程序
互斥锁
关于爬虫
1. 尽量减少请求次数
2. 手机App与H5反爬虫措施少一些
3. 防守方一般做到根据ip限制频次
4. 可使用多线程甚至分布式爬虫提高性能
关于反爬虫
1. 后台对访问进行统计,如果单个IP访问超过阈值,予以封锁
2. 后台对访问进行统计,如果单个session访问超过阈值,予以封锁。
3. 后台对访问进行统计,如果单个userAgent访问超过阈值,予以封锁。
4. 动态HtML:JavaScript,jQuery,Ajax,DHTML
测试模块
import time #导入python测试模块 import unittest #类名任意,但必须继承unittest.TestCase class DouyuTest(unittest.TestCase): #固定写法,通常做初始化 def setUp(self): print("setUp()....") self.num1 = 1 self.num2 = 1 def testTest(self): for i in range(1,3): print("testTest()==",i) self.num1 += 1 time.sleep(1) def testTest2(self): for i in range(1,3): print("testTest2()==",i) self.num2 += 1 time.sleep(1) #固定写法,但每个自定义方法接收后都会调用一次该方法 def tearDown(self): print("tearDown()...") print("num1==",self.num1) print("num2==", self.num2) if __name__ == "__main__": #调用的时候只需要写上main,固定的调用方式 unittest.main()