##爬虫 -通用网络爬虫 其的主要目的是将互联网上的资源下载到本地形成一个镜像备份。类似百度等搜索引擎 -聚焦爬虫 其面向特定需求的爬虫程序,与通用爬虫的区别在于对数据进行筛选尽量爬取相关数据 -爬虫优化 1.尽量减少请求次数 2.web页面不好爬取时爬app和h5页面(手机) ##HTTP和HTTPS --超文本传输协议 HTTP 端口80 HTTPS时加入了ssl安全传输协议 端口443 ##get请求方式获取 ---get方式一般用于向服务器获取数据 --parse用于url编码 实际使用是将url地址与想要查询的数据信息拼接形成完整地url 例如 # https://tieba.baidu.com/f?ie=utf-8&kw=火影忍者&fr=search # https://tieba.baidu.com/f?kw=火影忍者&ie=utf-8&pn=50 base_url = 'http://wwww.baidu.com/f?' # 搜索信息关键字 wd = input('搜索信息关键字') pn = input('pn值') q_data = { 'wd' = wd, 'pn' = pn, } # 对q_data进行编码使用parse模块 q_data = parse.urlencode(q_data) # 拼接url full_url = base_url + q_data # 构造请求对象 req = request.Request(url=full_url,headers=headers) # 获取响应对象 response = request.urlopen(req).read().decode('utf-8') ##post请求方式 --post方式需要向服务器发送一些数据 需要注意的是post传数据时不仅要使用parse转码还需要将其转化为字节形式 例如: base_url = 'http://wwww.baidu.com/' headers = { 'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:72.0) Gecko/20100101 Firefox/72.0", } # post携带数据 kw = input(">>") data = { 'kw': kw } parse_data = parse.urlencode(data) req = request.Request(url=base_url,data=bytes(data,encoding='utf-8'),headers=headers) response = request.urlopen(req).read().decode('utf-8') ##cookies --获取cookies cookies_object = cookiejar.CookieJar() cookies_handler = request.HTTPCookieProcessor(cookies_object) http_handler = request.HTTPHandler() https_handler = request.HTTPSHandler() opener = request.build_opener(http_handler,https_handler,cookies_handler)##ssl ---免认证 # 忽略未经核实的ssl证书认证 ssl._create_default_https_context = ssl._create_unverified_context() ##登录网页爬取开心网 ---登录url https://security.kaixin001.com/login/login_post.php 邮箱:loginemail 密码:password##有道翻译 ''' 分析: 爬取步骤 1 获取url 2 data数据填写 headers数据填写 3 构造request对象生成响应 data中的数据{ i "job" from "AUTO" to "AUTO" smartresult "dict" client "fanyideskweb" salt "15842516730329" sign "60f53618f3fa667e6d3192148c8c1a03" ts "1584251673032" bv "e2a78ed30c66e16a857c5b6486a1d326" doctype "json" version "2.1" keyfrom "fanyi.web" action "FY_BY_REALTlME" } headers中的数据{ Host: fanyi.youdao.com User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0 Accept: application/json, text/javascript, */*; q=0.01 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate # 此行使用时需要注释掉 不要接受压缩信息文件 Content-Type: application/x-www-form-urlencoded; charset=UTF-8 X-Requested-With: XMLHttpRequest Content-Length: 236 Origin: http://fanyi.youdao.com Connection: keep-alive Referer: http://fanyi.youdao.com/ # 我写代码时cookie有问题换了别人的cookie值可以正常爬取而我的会生成{‘errorcode’:50}错误 Cookie: OUTFOX_SEARCH_USER_ID=-140868279@123.121.59.79; OUTFOX_SEARCH_USER_ID_NCOO=1766192849.313334; _ntes_nnid=700fcdc75a16b68417175b615d961ea3,1537510225088; YOUDAO_MOBILE_ACCESS_TYPE=1; JSESSIONID=aaa_XsoMzWSA17Bi3OCdx; ___rl__test__cookies=1584251673023 } 难点 因为是post方式提交的数据所以在浏览器调试模式中查找请求方式为post的进行查看 post提交数据中i 为查询的数据 salt、sign和ts 会发生变化 获取salt和sign 的加密方式 在 fanyi.min.js 文件中使用代码格式化工具https://tool.oschina.net/codeformat/js 查找salt与sign关键字 salt: i i = r + parseInt(10 * Math.random(), 10); sign: n.md5("fanyideskweb" + e + i + "Nw(nmmbP%A-r6U3EUn]Aj") ts:r r = "" + (new Date).getTime(), ''' ##豆瓣电影分类排行 分析 爬取步骤 1.获取url信息 2.数据爬取 3.数据保存 页面分析一次显示20条信息 json数据中的请求url显示为: https://movie.douban.com/j/chart/top_list?type=11&interval_id=100%3A90&action=&start=0&limit=20 https://movie.douban.com/j/chart/top_list?type=11&interval_id=100%3A90&action=&start=20&limit=20 https://movie.douban.com/j/chart/top_list?type=11&interval_id=100%3A90&action=&start=40&limit=20 start 为起始点 limit 为显示信息 type 为剧情类型 难点: 需要爬取所有类型的type_id response = requests.get(url=url, headers=headers) douban_html = etree.HTML(response.text) datas = douban_html.xpath("//div[@class='article']/div[2]/div[@class='types']/span") for href in datas: info = href.xpath(".//@href")[0].split('&')[1] name = href.xpath(".//a")[0].text movies_type[name] = info##代理ip # ProxyHandler代理ip # 免费短期代理网站举例: # 西刺免费代理 IP:http://www.xicidaili.com/ # 快代理免费代理:https://www.kuaidaili.com/free/inha/ # 全网代理 IP:http://www.goubanjia.com/ ''' urllib中的request模块 1.定义代理对象 2.创建opener对象使用代理 3.构造请求对象 4.发送请求 ''' from urllib import request import random # 单个代理ip # proxy_ = request.ProxyHandler({ # # 代理ip # "http":'122.224.65.198:3128', # }) # 多个ip代理使用 也可以从文件中读取 proxy_list = [ {"http":'122.224.65.198:3128'}, {"http":'111.160.169.54:42626'}, {"http":'116.196.87.86:20183'}, {"http":'123.58.17.134:3128'}, ] proxy_ = random.choice(proxy_list) # 生成代理对象 proxy_handler = request.ProxyHandler(proxy_) opener = request.build_opener(proxy_handler) # 构造请求 req = request.Request(url='http://wwww.baidu.com/') # 发送请求 response = opener.open(req) ##西刺代理ip爬取 ##request模块使用 ####get方式 # 导入模块 import requests # get 无参 response = requests.get('url') # 还可以用request 方法request("请求方法",'url') response = requests.request("get",'url') # get 带参数 params参数接收字典或字符串 response = requests.get('url',params=dict1{},headers=headers) ####post方式 # 导入模块 import requests # data = { # # 提交数据 # } # response = requests.request("post",url='',data=data,headers=headers) #### ip代理 # proxies 参数 公开代理ip # proxies = [ # {"http": '122.224.65.198:3128'}, # {"http": '111.160.169.54:42626'}, # {"http": '116.196.87.86:20183'}, # {"http": '123.58.17.134:3128'}, # ] # response = requests.request('get',url='',params=dict1,headers=headers,proxies=proxies) # proxies 参数使用 私密代理 # proxy = {"http":"用户名:密码@ipd地址:端口"} # response = requests.request('get',url='',params=dict1,headers=headers,proxies=proxy) #### 获取数据后 response.text # 返回Unicode格式数据 response.content # 返回字节流数据用于保存二进制数据 response.json # 返回json文件 response.url # 返回完整url response.encoding # 返回响应编码格式 response.status_code # 返回响应码 ##cookies和session # cookies_obj = response.cookies # 获取cookies对象 # cookies_dict = response.utils.dict_from_cookiejar(cookies_obj) # 将cookies转换为字典 ''' session 对象获取cookies保存以便访问登录之后的页面 ''' # 生成session对象 session_obj = requests.session() # 发送post请求登录页面 form_data = { 'email':'', 'password':'', } session_obj.post(url='',data=form_data) # 此时cookies存储在session_obj中可以利用session_obj直接访问页面 response = session_obj.get(url='')##全国邮编爬取 设置解码格式防止出现乱码 response.encoding = 'gb2312' ''' https://www.ip138.com/post/ https://www.ip138.com/10/ 北京邮编url //table/tbody/tr[2] 北京邮编xpath ''' ##scrapy安装 -选择相应按本的twisted 进入https://www.lfd.uci.edu/~gohlke/pythonlibs/,下载对应的whl文件。cp后面是python版本,amd64代表64位win32代表32位 命令: pip install C:\Users\Administrator\Desktop\Twisted-19.10.0-cp38-cp38-win32.whl安装scrapy pip install -i https://pypi.doubanio.com/simple/ --trusted-host pypi.doubanio.com scrapy -建立项目 命令行输入: scrapy startproject 项目名 -scrapy框架结构与工作原理 引擎,调度器,下载器,爬虫,数据管道,中间件 spider发现URL生成request对提交给引擎随后进入调度器排队,之后进入下载器,下载器根据request对象的URL发送HTTP请求返回一个response对象 发送给spider的解析函数处理,将提取的数据封装成item提交给引擎,然后交给数据管道处理,其中若页面中有URL则重复以上步骤##框架解析 scrapy.cfg :项目的配置文件 mySpider/ :项目的 Python 模块,将会从这里引用代码 mySpider/items.py :项目的目标文件 mySpider/pipelines.py :项目的管道文件 mySpider/settings.py :项目的设置文件 mySpider/spiders/ :存储爬虫代码目录 ##items Item 定义结构化数据字段,用来保存爬取到的数据。明确存储信息。 可以通过创建一个 scrapy.Item 类, 并且定义类型为 scrapy.Field 的类属性来定义一个 Item(可以理解成类似于 ORM 的映射关系)。 例子: class MeiJuItem(scrapy.Item): '''爬取美剧 定义MeiJuItem类继承scrapy.Item定义存储数据 ''' name = scrapy.Field() href = scrapy.Field() state = scrapy.Field() tv = scrapy.Field() time = scrapy.Field()##spider -爬虫编写 步骤 1.导入scrapy库和相应item类 2.继承scrapy的spider类 3.name 用于唯一标识spider start_urls 用于存放需要爬取的URL 4.重写页面解析函数parse -parse方法 1.提取页面数据 2.提出新的页面请求 例子: import scrapy from example.items import MeiJuItem class MeiJuSpider(scrapy.Spider): name = 'meiju' start_urls = ["https://www.meijutt.tv/new100.html"] def parse(self, response): page_data = response.xpath(".//ul[@class='top-list fn-clear']/li") for li_data in page_data: item = MeiJuItem() item['name'] = li_data.xpath("./h5/a/@title").extract()[0] item['href'] = li_data.xpath("./h5/a/@href").extract()[0] item['state'] = li_data.xpath("./span[@class='state1 new100state1']/string()").extract()[0] item['tv'] = li_data.xpath("./span[@class='mjtv']/text()").extract()[0] item['time'] = li_data.xpath("./div[@class='lasted-time new100time fn-right']/text()").extract()[0] yield item##item pipeline以下是Item Pipeline的几种典型应用: ● 清洗数据。 ● 验证数据的有效性。 ● 过滤掉重复的数据。 ● 将数据存入数据库。 ● open_spider(self, spider) Spider打开时(处理数据前)回调该方法, 通常该方法用于 在开始处理数据之前完成某些初始化工作,如连接数据库。 ● close_spider(self, spider) Spider关闭时(处理数据后)回调该方法, 通常该方法用于 在处理完所有数据之后完成某些清理工作,如关闭数据库。 ● from_crawler(cls, crawler) 创建Item Pipeline对象时回调该类方法。 通常,在该方法中通过crawler.settings读取配置,根据配置创建Item Pipeline对象。 编写 item pipeline 很简单,item pipiline 组件是一个独立的 Python 类,其中 process_item() 方法必须实现 例子 class MeiJuPipeline(object): def __init__(self): # 可选实现,做参数初始化等 self.file = open('meiju.json','wb') pass def process_item(self,item,spider): ''' # item (Item 对象) – 被爬取的 item # spider (Spider 对象) – 爬取该 item 的 spider # 这个方法必须实现,每个 item pipeline 组件都需要调用该方法, # 这个方法必须返回一个 Item 对象,被丢弃的 item 将不会被之后的 pipeline 组件 所处理。 ''' json.doump(dict(item),open('meiju.json','a',encoding='utf-8'),ensure_ascii=False) return item pass def open_spider(self, spider): # spider (Spider 对象) – 被开启的 spider # 可选实现,当 spider 被开启时,这个方法被调用。 pass def close_spider(self,spider): # spider (Spider 对象) – 被关闭的 spider # 可选实现,当 spider 被关闭时,这个方法被调用 self.file.close()##log在setting中配置 LOG_FILE = "meiju.log" LOG_LEVEL = "INFO" ##middlewares中间键 DOWNLOADER_MIDDLEWARES = { 'scrapy_crawlera.CrawleraMiddleware': 600 }代理 Scrapy 代理 IP、Uesr-Agent 的切换都是通过 DOWNLOADER_MIDDLEWARES 进行控制 CRAWLERA_ENABLED = True CRAWLERA_USER = '注册/购买的 UserKey' CRAWLERA_PASS = '注册/购买的 Password 或者 修改 settings.py 配置 USER_AGENTS 和 PROXIES USER_AGENTS = [ "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)", "Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; WOW64; Trident/4.0; ] PROXIES = [ {'ip_port': '111.8.60.9:8123', 'user_passwd': 'user1:pass1'}, {'ip_port': '101.71.27.120:80', 'user_passwd': 'user2:pass2'}, {'ip_port': '122.96.59.104:80', 'user_passwd': 'user3:pass3'}, {'ip_port': '122.224.249.122:8088', 'user_passwd': 'user4:pass4'} ]下载延迟settings文件中配置 DOWNLOAD_DELAY = 3例子: import random import base64 from settings import USER_AGENTS from settings import PROXIES # 随机的 User-Agent class RandomUserAgent(object): def process_request(self, request, spider): useragent = random.choice(USER_AGENTS) request.headers.setdefault("User-Agent", useragent) class RandomProxy(object): def process_request(self, request, spider): proxy = random.choice(PROXIES) if proxy['user_passwd'] is None: # 没有代理账户验证的代理使用方式 request.meta['proxy'] = "http://" + proxy['ip_port'] else: # 对账户密码进行 base64 编码转换 base64_userpasswd = base64.b64encode(proxy['user_passwd']) # 对应到代理服务器的信令格式里 request.headers['Proxy-Authorization'] = 'Basic ' + base64_userpasswd request.meta['proxy'] = "http://" + proxy['ip_port'] ##settings在 scrapy 当中,可以在 scrapy.Request 中加入 dont_filter=False(默认去重)###xpath在 XPath 中,有七种类型的节点:元素、属性、文本、命名空间、处理指令、注释以及文档(根)节点。 XML 文档是被作为节点树来对待的。树的根被称为文档节点或者根节点。 --节点关系 --父节点 每个元素以及属性都有一个父。 在例子中book 元素是 title、author、year 以及 price 元素的父 --子节点 元素节点可有零个、一个或多个子。 在例子中title、author、year 以及 price 元素都是 book 元素的子 --同级节点 拥有相同的父的节点 在下面的例子中,title、author、year 以及 price 元素都是同级节点 -例子 <book> <title>Harry Potter</title> <author>J K. Rowling</author> <year>2005</year> <price>29.99</price> </book> --xpath语法 XPath 使用路径表达式在 XML 文档中选取节点。 nodename 选取此节点的所有子节点。 / 从根节点选取。 // 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。 . 选取当前节点。 .. 选取当前节点的父节点。 @ 选取属性。 --例子 bookstore 选取 bookstore 元素的所有子节点。 /bookstore 选取根元素 bookstore。注释:假如路径起始于正斜杠( / ),则此路径始终代表到某元素的绝对路径! bookstore/book 选取属于 bookstore 的子元素的所有 book 元素。 //book 选取所有 book 子元素,而不管它们在文档中的位置。 bookstore//book 选择属于 bookstore 元素的后代的所有 book 元素,而不管它们位于 bookstore 之下的什么位置。 //@lang 选取名为 lang 的所有属性。 --复杂例子 /bookstore/book[1] 选取属于 bookstore 子元素的第一个 book 元素。 /bookstore/book[last()] 选取属于 bookstore 子元素的最后一个 book 元素。 /bookstore/book[last()-1] 选取属于 bookstore 子元素的倒数第二个 book 元素。 /bookstore/book[position()<3] 选取最前面的两个属于 bookstore 元素的子元素的 book 元素。 //title[@lang] 选取所有拥有名为 lang 的属性的 title 元素。 //title[@lang='eng'] 选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性。 /bookstore/book[price>35.00] 选取 bookstore 元素的所有 book 元素,且其中的 price 元素的值须大于 35.00。 /bookstore/book[price>35.00]//title 选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35.00。 --xpath使用 导入lxml中的etree模块 使用etree的HTML方法将页面信息转化为xml文档 此时可以使用xml对象进行xpath数据提取###bs4 ---bs4自带转码功能不需要转码操作,其将HTML转换为树形结构 ---tag 对应 标签 使用 对象名.标签名 返回第一个满足条件的标签 name name属性获取标签名称 例如: name = soup.img.name attrs attrs属性获取标签的属性生成字典 例如: img = soup.img.attrs src = img.get("src") string string属性获取标签中的文本 例如: text = soup.title.string contents contents属性获取直接子节点返回一个列表 children children属性返回一个生成器 ---文档树搜索 find__all(name,attrs,recursive,text,**kwargs) name --标签名称 ----参数 字符串 -直接查询标签 正则表达式 -查询满足正则的所有标签 列表 -查询和列表中匹配的标签 attrs --标签属性 ----参数 属性名=值 -查询满足条件的标签 text --标签内容 ----参数和name一样接收字符串、正则表达式、列表 css选择器 类名前加 . id名前加 # 标签名不加修饰 select() 返回结果为列表 标签名查找 soup.select(标签名) 例如: soup.select('title')类名查找 soup.select('.类名') 例如: soup.select('.input') id名查找 soup.select('#id名') 例如: soup.select('#head_wrapper')组合查找 soup.select('div .input') soup.select('div #head_wrapper')属性查找 soup.select('div img[class="weixin"]')内容获取 使用get_text()方法 ---scrapy中 -标签提取 response.css('title')获取到一个selector列表例如: [<Selector xpath='descendant-or-self::title' data='<title>百度一下,你就知道</title>'>] -extract方法获取到标签列表 ['<title>百度一下,你就知道</title>'] -extract_first方法获取到标签 '<title>百度一下,你就知道</title>' -::text 获取到标签中的数据 response.css('title::text').extract_first() ---- response.css('标签名::text').extract_first() -class选择器 即<div class="quote post">中的class info.css('.text::text').extract_first()对象.css('标签.class名称::text').extract_first() -ID选择器 即<div id="quote">中的id info.css('#quote::text').extract_first()对象.css('标签#id名称::text').extract_first() --- 1.导入模块 from bs4 import BeautifulSoup 2.生成对象 soup=BeautifulSoup(html文档,'lxml') 3.格式化输出对象内容 content= soup.prettify()##re match 方法:从起始位置开始查找,一次匹配 search 方法:从任何位置开始查找,一次匹配 findall 方法:全部匹配,返回列表 finditer 方法:全部匹配,返回迭代器 split 方法:分割字符串,返回列表 sub 方法:替换 ^ 匹配字符串的开头 $ 匹配字符串的末尾。 . 匹配任意字符,除了换行符,当re.DOTALL标记被指定时,则可以匹配包括换行符的任意字符。 [...] 用来表示一组字符,单独列出:[amk] 匹配 'a','m'或'k' [^...] 不在[]中的字符:[^abc] 匹配除了a,b,c之外的字符 \w 匹配数字字母下划线 \W 匹配非数字字母下划线 \s 匹配任意空白字符,等价于 [\t\n\r\f]。 \S 匹配任意非空字符 \d 匹配任意数字,等价于 [0-9]。 \D 匹配任意非数字 * 匹配0个或多个的表达式。 + 匹配1个或多个的表达式。 ? 匹配0个或1个由前面的正则表达式定义的片段,非贪婪方式`##json模块用于对json类型与python类型相互转换 loads json.loads("json字符串") dumps json.dumps("python字符串") dump json.dump()
来源:https://www.cnblogs.com/lizhihoublog/p/12550202.html