strip相关知识
-
python笔记 strip()方法Python strip()方法描述Python strip() 方法用于移除字符串头尾指定的字符(默认为空格)。语法strip()方法语法:str.strip([chars]);参数chars -- 移除字符串头尾指定的字符。返回值返回移除字符串头尾指定的字符生成的新字符串。实例以下实例展示了strip()函数的使用方法:#!/usr/bin/pythonstr = "0000000this is string example....wow!!!0000000";print str.strip( '0' );以上实例输出结果如下:this is string example....wow!!!
-
Python中的strip()的理解在看到Python中strip的时候产生了疑问strip() 用于移除字符串头尾指定的字符(默认为空格)开始测试:>>> s = 'ncy_123.python'>>> s.strip('123')'ncy_123.python'疑问:明明指定要删除123,但是为什么返回值根本没有变,继续测试>>> s.strip('andyandc_3g1t2m')'.pytho'>>> s.strip('_3g1t2m')'ncy_123.python'两个测试返货结果不一样 这时候发现,字符串s在使用strip()的时候,只有在第一个字符被删掉之后,再去匹配后边的字符。原理应该是这样:s.strip('andya
-
Python strip lstrip rstrip使用方法Python中的strip用于去除字符串的首尾字符,同理,lstrip用于去除左边的字符,rstrip用于去除右边的字符。这三个函数都可传入一个参数,指定要去除的首尾字符。需要注意的是,传入的是一个字符数组,编译器去除两端所有相应的字符,直到没有匹配的字符,比如:theString = 'saaaay yes no yaaaass'print theString.strip('say')theString依次被去除首尾在['s','a','y']数组内的字符,直到字符在不数组内。所以,输出的结果为: yes no 比较简单吧,lstrip和rstrip原理是一样的。注意:当没有传入参数时,是默认去除首尾空格的。theString = 'saaaay yes no yaaaass'print theString.strip('say') print theString.strip(
-
python学习笔记——字符串方法center:用来在两边填充字符,让字符串居中'the middle is sssssss'.center(20)' The middle is lx 'strip:删除开头结尾空白字符' The middle is lx '.strip()'The middle is lx'names=['lx','dt','xx']>> name='lx '>> if name in names: print('this is true')..
strip相关课程
strip相关教程
- 3.6 如何删除字符串两侧多余的空格 您可以使用strip方法删除多余的空格:实例:# 删除两侧多余的空格extra_space = " Hello World "extra_space.strip# ---- 输出结果 ----"Hello World"经验:当提交表单中包含姓名、邮箱、手机号这些参数的时候,经常会用到这个方法。当您只想除去左侧或者右侧的多余空格时,您可以使用lstrip 和 rstrip。实例:extra_space = " Hello World "p extra_space.lstrip # 删除左侧多余的空格p extra_space.rstrip # 删除右侧多余的空格# ---- 输出结果 ----"Hello World "" Hello World"
- 2. 第一个基于 Scrapy 框架的爬虫 首先我们来看 Scrapy 项目的 spider 目录部分,新建一个 Python 文件,命名为:china_pub_crawler.py。这个文件中我们会用到 Scrapy 框架中非常重要的 Spider 类:class ChinaPubCrawler(Spider): name = "China-Pub-Crawler" start_urls = ["http://www.china-pub.com/Browse/"] def parse(self, response): pass # ...我们实现一个 ChinaPubCrawler 类,它继承了 Scrapy 框架的 Spider 类,在这里我们会用到 Spider 类的两个属性和一个方法:name: 爬虫名称,后续在运行 Scrapy 爬虫时会根据名称运行相应的爬虫;start_urls:开始要爬取的 URL 列表,这个地址可以动态调整;parse():该方法是默认的解析网页的回调方法。当然这里我们也可以自定义相应的函数来实现网页数据提取;我们思考下前面完成互动出版网的步骤:第一步是请求 http://www.china-pub.com/Browse/ 这个网页数据,从中找出计算机分类的链接 URL。这一步我们可以这样实现:class ChinaPubCrawler(Spider): name = "China-Pub-Crawler" start_urls = ["http://www.china-pub.com/Browse/"] def parse(self, response): """ 解析得到计算机互联网分类urls,然后重新构造请求 """ for url in response.xpath("//div[@id='wrap']/ul[1]/li[@class='li']/a/@href").getall(): # 封装请求,交给引擎去下载网页;注意设置处理解析网页的回调方法 yield Request(url, callback=self.book_list_parse) def book_list_parse(self, response): pass我们将起点爬取的 URL 设置为 http://www.china-pub.com/Browse/,然后使用默认的 parse() 解析这个网页的数据,提取到计算机分类的各个 URL 地址,然后使用 Scrapy 框架的 Request 类封装 URL 请求发送给 Scrapy 的 Engine 模块去继续下载网页。在 Request 类中我们可以设置请求网页的解析方法,这里我们会专门定义一个 book_list_parse() 类来解析图书列表的网页。为了能提取相应的图书信息数据,我们要定义对应的图书 Item 类,位于 items.py 文件中,代码内容如下:# -*- coding: utf-8 -*-import scrapyclass ChinaPubItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() title = scrapy.Field() book_url = scrapy.Field() author = scrapy.Field() isbn = scrapy.Field() publisher = scrapy.Field() publish_date = scrapy.Field() vip_price = scrapy.Field() price = scrapy.Field()这里正是我们前面定义的图书信息的 key 值,只不过这里用一种比较规范的方式进行了定义。现在还有一个问题要解决:如何实现分页的图书数据请求?我们在 book_list_parse() 方法中可以拿到当前解析的 URL,前面我们分析过:请求的 URL 中包含请求页信息。我们只需要将当前 URL 的页号加1,然后在构造 Request 请求交给 Scrapy 框架的引擎去执行即可,那么这样不会一直请求下去吗?我们只需要检查 response 的状态码,如果是 404,表示当前页号已经无效了,此时我们就不用再构造下一个的请求了,来看代码的实现:import refrom scrapy import Requestfrom scrapy.spiders import Spiderfrom ..items import ChinaPubItemclass ChinaPubCrawler(Spider): name = "China-Pub-Crawler" start_urls = ["http://www.china-pub.com/Browse/"] def parse(self, response): """ 解析得到计算机互联网分类urls,然后重新构造请求 """ for url in response.xpath("//div[@id='wrap']/ul[1]/li[@class='li']/a/@href").getall(): yield Request(url, callback=self.book_list_parse) def book_list_parse(self, response): # 如果返回状态码为404,直接返回 if response.status == 404: return # 解析当前网页的图书列表数据 book_list = response.xpath("//div[@class='search_result']/table/tr/td[2]/ul") for book in book_list: item = ChinaPubItem() item['title'] = book.xpath("li[@class='result_name']/a/text()").extract_first() item['book_url'] = book.xpath("li[@class='result_name']/a/@href").extract_first() book_info = book.xpath("./li[2]/text()").extract()[0] item['author'] = book_info.split('|')[0].strip() item['publisher'] = book_info.split('|')[1].strip() item['isbn'] = book_info.split('|')[2].strip() item['publish_date'] = book_info.split('|')[3].strip() item['vip_price'] = book.xpath("li[@class='result_book']/ul/li[@class='book_dis']/text()").extract()[0] item['price'] = book.xpath("li[@class='result_book']/ul/li[@class='book_price']/text()").extract()[0] yield item # 生成下一页url,交给Scrapy引擎再次发送请求 url = response.url regex = "(http://.*/)([0-9]+)_(.*).html" pattern = re.compile(regex) m = pattern.match(url) if not m: return [] prefix_path = m.group(1) current_page = m.group(2) suffix_path = m.group(3) next_page = int(current_page) + 1 next_url = f"{prefix_path}{next_page}_{suffix_path}.html" print("下一个url为:{}".format(next_url)) yield Request(next_url, callback=self.book_list_parse)请求所有的 URL,解析相应数据,这些我们都有了,还差最后一步:数据入库!这一步我们使用 item Pipeline 来实现将得到的 item 数据导入到 MongoDB 中。编写的 item Pipeline 一般写在 pipelines.py 中,来看看代码样子:import pymongoclass ChinaPubPipeline: def open_spider(self, spider): """连接mongodb,并认证连接信息,内网ip""" self.client = pymongo.MongoClient(host='192.168.88.204', port=27017) self.client.admin.authenticate("admin", "shencong1992") db = self.client.scrapy_manual # 新建一个集合保存抓取到的图书数据 self.collection = db.china_pub_scrapy def process_item(self, item, spider): # 处理item数据 try: book_info = { 'title': item['title'], 'book_url': item['book_url'], 'author': item['author'], 'isbn': item['isbn'], 'publisher': item['publisher'], 'publish_date': item['publish_date'], 'vip_price': item['vip_price'], 'price': item['price'], } self.collection.insert_one(book_info) except Exception as e: print("插入数据异常:{}".format(str(e))) return item def close_spider(self, spider): # 关闭连接 self.client.close()最后为了使这个 pipeline 生效,我们需要将这个 pipeline 写到 settings.py 文件中:# settings.py# ...ITEM_PIPELINES = { 'china_pub.pipelines.ChinaPubPipeline': 300,}# ...最后,我们还需要在请求的头部加上一个 User-Agent 参数,这个设置在 settings.py 中完成:# settings.py# ...USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36'# ...整个爬虫代码就基本完成了,接下来开始我们激动人心的数据爬取吧!
- 2. Django 中的内置 Field BooleanField:之前演示过,它会被渲染成前端的 checkbox 组件。从源码上看它似乎没有额外特殊的属性。主要就是继承了 Field 类,然后重写了 to_python() 等方法class BooleanField(Field): widget = CheckboxInput def to_python(self, value): """Return a Python boolean object.""" # Explicitly check for the string 'False', which is what a hidden field # will submit for False. Also check for '0', since this is what # RadioSelect will provide. Because bool("True") == bool('1') == True, # we don't need to handle that explicitly. if isinstance(value, str) and value.lower() in ('false', '0'): value = False else: value = bool(value) return super().to_python(value) def validate(self, value): if not value and self.required: raise ValidationError(self.error_messages['required'], code='required') def has_changed(self, initial, data): if self.disabled: return False # Sometimes data or initial may be a string equivalent of a boolean # so we should run it through to_python first to get a boolean value return self.to_python(initial) != self.to_python(data)CharField:用的最多的,会被渲染成输入框,我们可以通过 widget 属性值控制输入框样式等。这在前面的登录表单例子中也是演示过的。 name = forms.CharField( label="账号", min_length=4, required=True, error_messages={'required': '账号不能为空', "min_length": "账号名最短4位"}, widget=forms.TextInput(attrs={'class': "input-text", 'placeholder': '请输入登录账号'}) )class CharField(Field): def __init__(self, *, max_length=None, min_length=None, strip=True, empty_value='', **kwargs): self.max_length = max_length self.min_length = min_length self.strip = strip self.empty_value = empty_value super().__init__(**kwargs) if min_length is not None: self.validators.append(validators.MinLengthValidator(int(min_length))) if max_length is not None: self.validators.append(validators.MaxLengthValidator(int(max_length))) self.validators.append(validators.ProhibitNullCharactersValidator()) def to_python(self, value): """Return a string.""" if value not in self.empty_values: value = str(value) # 是否去掉首尾空格 if self.strip: value = value.strip() if value in self.empty_values: return self.empty_value return value def widget_attrs(self, widget): attrs = super().widget_attrs(widget) if self.max_length is not None and not widget.is_hidden: # The HTML attribute is maxlength, not max_length. attrs['maxlength'] = str(self.max_length) if self.min_length is not None and not widget.is_hidden: # The HTML attribute is minlength, not min_length. attrs['minlength'] = str(self.min_length) return attrs除了 Field 属性外,CharField 还有 max_length 和 min_length 等属性。这些也都会反映在渲染的 input 元素上,同时校验器也会添加该属性的校验:if min_length is not None: self.validators.append(validators.MinLengthValidator(int(min_length)))if max_length is not None: self.validators.append(validators.MaxLengthValidator(int(max_length)))CharField 还是很多 Field 类的父类,比如 RegexField、EmailField 等。ChoiceField:前面我们在 widget 属性的小实验中看到了 ChoiceField 对应的 widget 属性值是 Select 类,也即对应 select 元素。我们继续使用前面的登录表单来演示下这个 ChoiceField 类。我们除了添加 Field 之外,也需要添加下前端代码,如下所示。{% load staticfiles %}<link rel="stylesheet" type="text/css" href="{% static 'css/main.css' %}" />{% if not success %}<form action="/hello/test_form_view2/" method="POST">{% csrf_token %}<div><span>{{ form.name.label }}:</span>{{ form.name }}<div><span>{{ form.password.label }}:</span>{{ form.password }}<!------- 新增login_type字段的HTML -------------><div><span>{{ form.login_type.label }}:</span>{{ form.login_type }}<!--------------------------------------------><div>{{ form.save_login }}{{ form.save_login.label }}</div><div><input class="input-text input-red" type="submit" value="登录" style="width: 214px"/></div>{% if err_msg %}<div><label class="color-red">{{ err_msg }}</label</div>{% endif %}</form>{% else %}<p>登录成功</p>{% endif %}login_type = forms.ChoiceField( label="账号类型", required=True, initial=1, choices=((0, '普通用户'), (1, '管理员'), (2, '其他')), error_messages={'required': '必选类型' }, widget=forms.Select(attrs={'class': "input-text"}), )效果图如下所示。可以看到,这里 initial 属性值表示最开始选中那个选项,而 choices 属性值是一个元组,表示多选框的显示 name 值和实际 value 值。DateField:默认的小部件是 DateInput。它有一个比较重要的属性:input_formats,用于将字符串转换为有效datetime.date对象的格式列表。如果没有提供 input_formats 参数,则默认的格式为:['%Y-%m-%d', # '2006-10-25' '%m/%d/%Y', # '10/25/2006' '%m/%d/%y'] # '10/25/06'class DateField(BaseTemporalField): widget = DateInput input_formats = formats.get_format_lazy('DATE_INPUT_FORMATS') default_error_messages = { 'invalid': _('Enter a valid date.'), } def to_python(self, value): """ Validate that the input can be converted to a date. Return a Python datetime.date object. """ if value in self.empty_values: return None if isinstance(value, datetime.datetime): return value.date() if isinstance(value, datetime.date): return value return super().to_python(value) def strptime(self, value, format): return datetime.datetime.strptime(value, format).date()DateTimeField:默认的小部件是 DateTimeInput,这里会校验对应的值是否是datetime.datetime、 datetime.date类型,或者按照 input_formats 参数格式化的字符串。同样,如果没有提供 input_formats 参数,则默认的格式为:['%Y-%m-%d %H:%M:%S', # '2006-10-25 14:30:59' '%Y-%m-%d %H:%M', # '2006-10-25 14:30' '%Y-%m-%d', # '2006-10-25' '%m/%d/%Y %H:%M:%S', # '10/25/2006 14:30:59' '%m/%d/%Y %H:%M', # '10/25/2006 14:30' '%m/%d/%Y', # '10/25/2006' '%m/%d/%y %H:%M:%S', # '10/25/06 14:30:59' '%m/%d/%y %H:%M', # '10/25/06 14:30' '%m/%d/%y'] # '10/25/06'class DateTimeField(BaseTemporalField): widget = DateTimeInput input_formats = formats.get_format_lazy('DATETIME_INPUT_FORMATS') default_error_messages = { 'invalid': _('Enter a valid date/time.'), } def prepare_value(self, value): if isinstance(value, datetime.datetime): value = to_current_timezone(value) return value def to_python(self, value): """ Validate that the input can be converted to a datetime. Return a Python datetime.datetime object. """ if value in self.empty_values: return None if isinstance(value, datetime.datetime): return from_current_timezone(value) if isinstance(value, datetime.date): result = datetime.datetime(value.year, value.month, value.day) return from_current_timezone(result) result = super().to_python(value) return from_current_timezone(result) def strptime(self, value, format): return datetime.datetime.strptime(value, format)这些类的定义都是比较简单的,都是基于 Field 类,有的基于 CharField 类等。后面我们会重点分析 Field 类。EmailField:EmailField 直接继承 CharField,它和 CharField 的一个主要区别就是多加了一个默认的校验器,主要校验输入的值是否是邮箱格式。其实现的代码如下:class EmailField(CharField): widget = EmailInput default_validators = [validators.validate_email] def __init__(self, **kwargs): super().__init__(strip=True, **kwargs)IntegerField:对应的小部件是 NumberInput,输入整数字符串。它可以输入 min_Value、max_value 等参数用于控制输入值的范围。其源码如下,和 CharFiled 类的代码比较类似。class IntegerField(Field): widget = NumberInput default_error_messages = { 'invalid': _('Enter a whole number.'), } re_decimal = re.compile(r'\.0*\s*$') def __init__(self, *, max_value=None, min_value=None, **kwargs): self.max_value, self.min_value = max_value, min_value if kwargs.get('localize') and self.widget == NumberInput: # Localized number input is not well supported on most browsers kwargs.setdefault('widget', super().widget) super().__init__(**kwargs) if max_value is not None: self.validators.append(validators.MaxValueValidator(max_value)) if min_value is not None: self.validators.append(validators.MinValueValidator(min_value)) def to_python(self, value): """ Validate that int() can be called on the input. Return the result of int() or None for empty values. """ value = super().to_python(value) if value in self.empty_values: return None if self.localize: value = formats.sanitize_separators(value) # Strip trailing decimal and zeros. try: value = int(self.re_decimal.sub('', str(value))) except (ValueError, TypeError): raise ValidationError(self.error_messages['invalid'], code='invalid') return value def widget_attrs(self, widget): attrs = super().widget_attrs(widget) if isinstance(widget, NumberInput): if self.min_value is not None: attrs['min'] = self.min_value if self.max_value is not None: attrs['max'] = self.max_value return attrs对于 IntegerField 字段输入的值,看 to_python() 方法,首先对于 20.0 这样的形式会先去掉后面的 .0,然后用 int() 方法强转,如果发生异常,那就表明该字段对应的值不是整数,然后可以抛出异常。DecimalField:它继承自 IntegerField,用于输入浮点数。它有如下几个重要参数:max_value: 最大值min_value: 最小值max_digits: 总长度decimal_places: 小数位数来看看它的定义的代码,如下:class DecimalField(IntegerField): default_error_messages = { 'invalid': _('Enter a number.'), } def __init__(self, *, max_value=None, min_value=None, max_digits=None, decimal_places=None, **kwargs): self.max_digits, self.decimal_places = max_digits, decimal_places super().__init__(max_value=max_value, min_value=min_value, **kwargs) self.validators.append(validators.DecimalValidator(max_digits, decimal_places)) def to_python(self, value): """ Validate that the input is a decimal number. Return a Decimal instance or None for empty values. Ensure that there are no more than max_digits in the number and no more than decimal_places digits after the decimal point. """ if value in self.empty_values: return None if self.localize: value = formats.sanitize_separators(value) value = str(value).strip() try: # 使用Decimal()方法转换类型 value = Decimal(value) except DecimalException: raise ValidationError(self.error_messages['invalid'], code='invalid') return value def validate(self, value): super().validate(value) if value in self.empty_values: return if not value.is_finite(): raise ValidationError(self.error_messages['invalid'], code='invalid') def widget_attrs(self, widget): attrs = super().widget_attrs(widget) if isinstance(widget, NumberInput) and 'step' not in widget.attrs: if self.decimal_places is not None: # Use exponential notation for small values since they might # be parsed as 0 otherwise. ref #20765 step = str(Decimal(1).scaleb(-self.decimal_places)).lower() else: step = 'any' attrs.setdefault('step', step) return attrs可以看到在 to_python() 方法中,最后对该字段输入的值使用 Decimal() 方法进行类型转换 。FloatField:用于渲染成一个只允许输入浮点数的输入框。它同样继承自 IntegerField,因此它对应的小部件也是 NumberInput。class FloatField(IntegerField): default_error_messages = { 'invalid': _('Enter a number.'), } def to_python(self, value): """ Validate that float() can be called on the input. Return the result of float() or None for empty values. """ value = super(IntegerField, self).to_python(value) if value in self.empty_values: return None if self.localize: value = formats.sanitize_separators(value) try: value = float(value) except (ValueError, TypeError): raise ValidationError(self.error_messages['invalid'], code='invalid') return value def validate(self, value): super().validate(value) if value in self.empty_values: return if not math.isfinite(value): raise ValidationError(self.error_messages['invalid'], code='invalid') def widget_attrs(self, widget): attrs = super().widget_attrs(widget) if isinstance(widget, NumberInput) and 'step' not in widget.attrs: attrs.setdefault('step', 'any') return attrs其余的 Field 类如 ImageField,RegexField 就不一一描述了,具体可以参考官方文档以及相应的源码进行学习。
- 3. 图书爬虫之代码实现 根据上面的分析,我们来实现相应的代码。首先是完成获取计算机的所有分类以及相应的 URL 地址:def get_all_computer_book_urls(page_url): """ 获取所有计算机分类图书的url地址 :return: """ response = requests.get(url=page_url, headers=headers) if response.status_code != 200: return [], [] response.encoding = 'gbk' tree = etree.fromstring(response.text, etree.HTMLParser()) # 提取计算机分类的文本列表 c = tree.xpath("//div[@id='wrap']/ul[1]/li[@class='li']/a/text()") # 提取计算机分类的url列表 u = tree.xpath("//div[@id='wrap']/ul[1]/li[@class='li']/a/@href") return c, u我们简单测试下这个函数:[store@server2 chap06]$ python3Python 3.6.8 (default, Apr 2 2020, 13:34:55) [GCC 4.8.5 20150623 (Red Hat 4.8.5-39)] on linuxType "help", "copyright", "credits" or "license" for more information.>>> from china_pub_crawler import get_all_computer_book_urls>>> get_all_computer_book_urls('http://www.china-pub.com/Browse/')(['IT图书网络出版 [59-00]', '计算机科学理论与基础知识 [59-01]', '计算机组织与体系结构 [59-02]', '计算机网络 [59-03]', '安全 [59-04]', '软件与程序设计 [59-05]', '软件工程及软件方法学 [59-06]', '操作系统 [59-07]', '数据库 [59-08]', '硬件与维护 [59-09]', '图形图像、多媒体、网页制作 [59-10]', '中文信息处理 [59-11]', '计算机辅助设计与工程计算 [59-12]', '办公软件 [59-13]', '专用软件 [59-14]', '人工智能 [59-15]', '考试认证 [59-16]', '工具书 [59-17]', '计算机控制与仿真 [59-18]', '信息系统 [59-19]', '电子商务与计算机文化 [59-20]', '电子工程 [59-21]', '期刊 [59-22]', '游戏 [59-26]', 'IT服务管理 [59-27]', '计算机文化用品 [59-80]'], ['http://product.china-pub.com/cache/browse2/59/1_1_59-00_0.html', 'http://product.china-pub.com/cache/browse2/59/1_1_59-01_0.html', 'http://product.china-pub.com/cache/browse2/59/1_1_59-02_0.html', 'http://product.china-pub.com/cache/browse2/59/1_1_59-03_0.html', 'http://product.china-pub.com/cache/browse2/59/1_1_59-04_0.html', 'http://product.china-pub.com/cache/browse2/59/1_1_59-05_0.html', 'http://product.china-pub.com/cache/browse2/59/1_1_59-06_0.html', 'http://product.china-pub.com/cache/browse2/59/1_1_59-07_0.html', 'http://product.china-pub.com/cache/browse2/59/1_1_59-08_0.html', 'http://product.china-pub.com/cache/browse2/59/1_1_59-09_0.html', 'http://product.china-pub.com/cache/browse2/59/1_1_59-10_0.html', 'http://product.china-pub.com/cache/browse2/59/1_1_59-11_0.html', 'http://product.china-pub.com/cache/browse2/59/1_1_59-12_0.html', 'http://product.china-pub.com/cache/browse2/59/1_1_59-13_0.html', 'http://product.china-pub.com/cache/browse2/59/1_1_59-14_0.html', 'http://product.china-pub.com/cache/browse2/59/1_1_59-15_0.html', 'http://product.china-pub.com/cache/browse2/59/1_1_59-16_0.html', 'http://product.china-pub.com/cache/browse2/59/1_1_59-17_0.html', 'http://product.china-pub.com/cache/browse2/59/1_1_59-18_0.html', 'http://product.china-pub.com/cache/browse2/59/1_1_59-19_0.html', 'http://product.china-pub.com/cache/browse2/59/1_1_59-20_0.html', 'http://product.china-pub.com/cache/browse2/59/1_1_59-21_0.html', 'http://product.china-pub.com/cache/browse2/59/1_1_59-22_0.html', 'http://product.china-pub.com/cache/browse2/59/1_1_59-26_0.html', 'http://product.china-pub.com/cache/browse2/59/1_1_59-27_0.html', 'http://product.china-pub.com/cache/browse2/59/1_1_59-80_0.html'])可以看到这个函数已经实现了我们想要的结果。接下来我们要完成一个函数来获取对应分类下的所有图书信息,不过在此之前,我们需要先完成解析单个图书列表页面的方法:def parse_books_page(html_data): books = [] tree = etree.fromstring(html_data, etree.HTMLParser()) result_tree = tree.xpath("//div[@class='search_result']/table/tr/td[2]/ul") for result in result_tree: try: book_info = {} book_info['title'] = result.xpath("./li[@class='result_name']/a/text()")[0] book_info['book_url'] = result.xpath("./li[@class='result_name']/a/@href")[0] info = result.xpath("./li[2]/text()")[0] book_info['author'] = info.split('|')[0].strip() book_info['publisher'] = info.split('|')[1].strip() book_info['isbn'] = info.split('|')[2].strip() book_info['publish_date'] = info.split('|')[3].strip() book_info['vip_price'] = result.xpath("./li[@class='result_book']/ul/li[@class='book_dis']/text()")[0] book_info['price'] = result.xpath("./li[@class='result_book']/ul/li[@class='book_price']/text()")[0] # print(f'解析出的图书信息为:{book_info}') books.append(book_info) except Exception as e: print("解析数据出现异常,忽略!") return books上面的函数主要解析的是一页图书列表数据,同样基于 xpath 定位相应的元素,然后提取我们想要的数据。其中由于部分信息合在一起,我们在提取数据后还要做相关的处理,分别提取对应的信息。我们可以从网页中直接样 HTML 拷贝下来,然后对该函数进行测试:提取图书列表的网页数据我们把保存的网页命名为 test.html,放到与该代码同级的目录下,然后进入命令行操作:>>> from china_pub_crawler import parse_books_page>>> f = open('test.html', 'r+')>>> html_content = f.read()>>> parse_books_page(html_content)[{'title': '(特价书)零基础学ASP.NET 3.5', 'book_url': 'http://product.china-pub.com/216269', 'author': '王向军;王欣惠 (著)', 'publisher': '机械工业出版社', 'isbn': '9787111261414', 'publish_date': '2009-02-01出版', 'vip_price': 'VIP会员价:', 'price': '¥58.00'}, {'title': 'Objective-C 2.0 Mac和iOS开发实践指南(原书第2版)', 'book_url': 'http://product.china-pub.com/3770704', 'author': '(美)Robert Clair (著)', 'publisher': '机械工业出版社', 'isbn': '9787111484561', 'publish_date': '2015-01-01出版', 'vip_price': 'VIP会员价:', 'price': '¥79.00'}, {'title': '(特价书)ASP.NET 3.5实例精通', 'book_url': 'http://product.china-pub.com/216272', 'author': '王院峰 (著)', 'publisher': '机械工业出版社', 'isbn': '9787111259794', 'publish_date': '2009-01-01出版', 'vip_price': 'VIP会员价:', 'price': '¥55.00'}, {'title': '(特价书)CSS+HTML语法与范例详解词典', 'book_url': 'http://product.china-pub.com/216275', 'author': '符旭凌 (著)', 'publisher': '机械工业出版社', 'isbn': '9787111263647', 'publish_date': '2009-02-01出版', 'vip_price': 'VIP会员价:', 'price': '¥39.00'}, {'title': '(特价书)Java ME 游戏编程(原书第2版)', 'book_url': 'http://product.china-pub.com/216296', 'author': '(美)Martin J. Wells; John P. Flynt (著)', 'publisher': '机械工业出版社', 'isbn': '9787111264941', 'publish_date': '2009-03-01出版', 'vip_price': 'VIP会员价:', 'price': '¥49.00'}, {'title': '(特价书)Visual Basic实例精通', 'book_url': 'http://product.china-pub.com/216304', 'author': '柴相花 (著)', 'publisher': '机械工业出版社', 'isbn': '9787111263296', 'publish_date': '2009-04-01出版', 'vip_price': 'VIP会员价:', 'price': '¥59.80'}, {'title': '高性能电子商务平台构建:架构、设计与开发[按需印刷]', 'book_url': 'http://product.china-pub.com/3770743', 'author': 'ShopNC产品部 (著)', 'publisher': '机械工业出版社', 'isbn': '9787111485643', 'publish_date': '2015-01-01出版', 'vip_price': 'VIP会员价:', 'price': '¥79.00'}, {'title': '[套装书]Java核心技术 卷Ⅰ 基础知识(原书第10版)+Java核心技术 卷Ⅱ高级特性(原书第10版)', 'book_url': 'http://product.china-pub.com/7008447', 'author': '(美)凯S.霍斯特曼(Cay S. Horstmann)????(美)凯S. 霍斯特曼(Cay S. Horstmann) (著)', 'publisher': '机械工业出版社', 'isbn': '9787007008447', 'publish_date': '2017-08-01出版', 'vip_price': 'VIP会员价:', 'price': '¥258.00'}, {'title': '(特价书)Dojo构建Ajax应用程序', 'book_url': 'http://product.china-pub.com/216315', 'author': '(美)James E.Harmon (著)', 'publisher': '机械工业出版社', 'isbn': '9787111266648', 'publish_date': '2009-05-01出版', 'vip_price': 'VIP会员价:', 'price': '¥45.00'}, {'title': '(特价书)编译原理第2版.本科教学版', 'book_url': 'http://product.china-pub.com/216336', 'author': '(美)Alfred V. Aho;Monica S. Lam;Ravi Sethi;Jeffrey D. Ullman (著)', 'publisher': '机械工业出版社', 'isbn': '9787111269298', 'publish_date': '2009-05-01出版', 'vip_price': 'VIP会员价:', 'price': '¥55.00'}, {'title': '(特价书)用Alice学编程(原书第2版)', 'book_url': 'http://product.china-pub.com/216354', 'author': '(美)Wanda P.Dann;Stephen Cooper;Randy Pausch (著)', 'publisher': '机械工业出版社', 'isbn': '9787111274629', 'publish_date': '2009-07-01出版', 'vip_price': 'VIP会员价:', 'price': '¥39.00'}, {'title': 'Java语言程序设计(第2版)', 'book_url': 'http://product.china-pub.com/50051', 'author': '赵国玲;王宏;柴大鹏 (著)', 'publisher': '机械工业出版社*', 'isbn': '9787111297376', 'publish_date': '2010-03-01出版', 'vip_price': 'VIP会员价:', 'price': '¥32.00'}, {'title': '从零开始学Python程序设计', 'book_url': 'http://product.china-pub.com/7017939', 'author': '吴惠茹 (著)', 'publisher': '机械工业出版社', 'isbn': '9787111583813', 'publish_date': '2018-01-01出版', 'vip_price': 'VIP会员价:', 'price': '¥79.00'}, {'title': '(特价书)汇编语言', 'book_url': 'http://product.china-pub.com/216385', 'author': '郑晓薇 (著)', 'publisher': '机械工业出版社', 'isbn': '9787111269076', 'publish_date': '2009-09-01出版', 'vip_price': 'VIP会员价:', 'price': '¥29.00'}, {'title': '(特价书)Visual Basic.NET案例教程', 'book_url': 'http://product.china-pub.com/216388', 'author': '马玉春;刘杰民;王鑫 (著)', 'publisher': '机械工业出版社', 'isbn': '9787111272571', 'publish_date': '2009-09-01出版', 'vip_price': 'VIP会员价:', 'price': '¥30.00'}, {'title': '小程序从0到1:微信全栈工程师一本通', 'book_url': 'http://product.china-pub.com/7017943', 'author': '石桥码农 (著)', 'publisher': '机械工业出版社', 'isbn': '9787111584049', 'publish_date': '2018-01-01出版', 'vip_price': 'VIP会员价:', 'price': '¥59.00'}, {'title': '深入分布式缓存:从原理到实践', 'book_url': 'http://product.china-pub.com/7017945', 'author': '于君泽 (著)', 'publisher': '机械工业出版社', 'isbn': '9787111585190', 'publish_date': '2018-01-01出版', 'vip_price': 'VIP会员价:', 'price': '¥99.00'}, {'title': '(特价书)ASP.NET AJAX服务器控件高级编程(.NET 3.5版)', 'book_url': 'http://product.china-pub.com/216397', 'author': '(美)Adam Calderon;Joel Rumerman (著)', 'publisher': '机械工业出版社', 'isbn': '9787111270966', 'publish_date': '2009-09-01出版', 'vip_price': 'VIP会员价:', 'price': '¥65.00'}, {'title': 'PaaS程序设计', 'book_url': 'http://product.china-pub.com/3770830', 'author': '(美)Lucas Carlson (著)', 'publisher': '机械工业出版社', 'isbn': '9787111482451', 'publish_date': '2015-01-01出版', 'vip_price': 'VIP会员价:', 'price': '¥39.00'}, {'title': 'Visual C++数字图像处理[按需印刷]', 'book_url': 'http://product.china-pub.com/2437', 'author': '何斌 马天予 王运坚 朱红莲 (著)', 'publisher': '人民邮电出版社', 'isbn': '711509263X', 'publish_date': '2001-04-01出版', 'vip_price': 'VIP会员价:', 'price': '¥72.00'}]是不是能正确提取图书列表的相关信息?这也说明我们的函数的正确性,由于也可能在解析中存在一些异常,比如某个字段的缺失,我们需要捕获异常并忽略该条数据,让程序能继续走下去而不是停止运行。在完成了上述的工作后,我们来通过对页号的 URL 构造,实现采集多个分页下的数据,最后达到读取完该分类下的所有图书信息的目的。完整代码如下:def get_category_books(category, url): """ 获取类别图书,下面会有分页,我们一直请求,直到分页请求返回404即可停止 :return: """ books = [] page = 1 regex = "(http://.*/)([0-9]+)_(.*).html" pattern = re.compile(regex) m = pattern.match(url) if not m: return [] prefix_path = m.group(1) current_page = m.group(2) if current_page != 1: print("提取数据不是从第一行开始,可能存在问题") suffix_path = m.group(3) current_page = page while True: # 构造分页请求的URL book_url = f"{prefix_path}{current_page}_{suffix_path}.html" response = requests.get(url=book_url, headers=headers) print(f"提取分类[{category}]下的第{current_page}页图书数据") if response.status_code != 200: print(f"[{category}]该分类下的图书数据提取完毕!") break response.encoding = 'gbk' # 将该分页的数据加到列表中 books.extend(parse_books_page(response.text)) current_page += 1 # 一定要缓一缓,避免对对方服务造成太大压力 time.sleep(0.5) return books最后保存数据到 MongoDB 中,这一步非常简单,我们前面已经操作过 MongoDB 的文档插入,直接搬用即可:client = pymongo.MongoClient(host='MongoDB的服务地址', port=27017)client.admin.authenticate("admin", "shencong1992")db = client.scrapy_manualcollection = db.china_pub# ...def save_to_mongodb(data): try: collection.insert_many(data) except Exception as e: print("批量插入数据异常:{}".format(str(e)))正是由于我们前面生成了批量的 json 数据,这里直接使用集合的 insert_many() 方法即可对采集到的数据批量插入 MongoDB 中。代码的最后我们加上一个 main 函数即可:# ...if __name__ == '__main__': page_url = "http://www.china-pub.com/Browse/" categories, urls = get_all_computer_book_urls(page_url) # print(categories) books_total = {} for i in range(len(urls)): books_category_data = get_category_books(categories[i], urls[i]) print(f"保存[{categories[i]}]图书数据到mongodb中") save_to_mongodb(books_category_data) print("爬取互动出版网的计算机分类数据完成")这样一个简单的爬虫就完成了,还等什么,开始跑起来吧!!
- 2.1 影响字符串长度或内容的函数 序号函数说明 1str.split() 拆分字符串 2str.slice() 按给定的位置截取字符串 3str.strip() 删除字符串中的空格或者换行符 4str.lower() 将字符串转换为小写 5str.upper() 将字符串转换为大写 6str.cat() 连接字符串列 7str.replace() 替换字符串中的元素 8str.swapcase() 字符串中的大小写互换下面我们将通过代码操作详细介绍这几个函数的使用方法:1. split() 函数该函数根据指定的元素分割字符串。# 导入pandas包import pandas as pddata_path="C:/Users/13965/Documents/myFuture/IMOOC/pandasCourse-progress/data_source/第16小节/execl数据demo.xlsx"# 解析数据data = pd.read_excel(data_path)print(data)# --- 输出结果 --- 编程语言 推出时间 价格 主要创始人0 java 1995年 45.6 James Gosling1 python@163.com 1991年 67.0 Guido van Rossum\n2 C 1972年 33.9 Dennis MacAlistair Ritchie3 js@qq.com 1995年 59.5 Brendan Eich4 php 2012年\n 69.9 Rasmus Lerdorf5 C++ 1983年 75.0 Bjarne Stroustrup# split() 函数new_series=data["编程语言"]split_res= new_series.str.split("@")print(split_res)# --- 输出结果 ---0 [java]1 [python, 163.com]2 [C]3 [js, qq.com]4 [php]5 [C++ ]Name: 编程语言, dtype: object结果解析:首先我们获得 DataFrame 的 “编程语言” 列,返回的数据结构为 Series ,我们通过 str 属性的 split () 函数,传入分割依据的字符,通过结果可以看到,存在 @ 字符的字符串均被分割成了多个子元素,返回结果也是由列表构成的 Series 数据集。2. slice() 函数该函数根据给定开始和结束的下标位置,截取字符串,注意是左闭右开,不包含右边的下标位置,该函数有三参数:start :开始下标 stop :结束下标 step :步长# --- 解析 data 数据结果 --- 编程语言 推出时间 价格 主要创始人0 java 1995年 45.6 James Gosling1 python@163.com 1991年 67.0 Guido van Rossum\n2 C 1972年 33.9 Dennis MacAlistair Ritchie3 js@qq.com 1995年 59.5 Brendan Eich4 php 2012年\n 69.9 Rasmus Lerdorf5 C++ 1983年 75.0 Bjarne Stroustru# data 解析出来的数据# slice() 函数new_series=data["编程语言"]slice_res= new_series.str.slice(2,4)print(slice_res)# --- 输出结果 ---0 va1 th2 3 @q4 p5 + Name: 编程语言, dtype: object结果解析:这里我们指定 "编程语言" 列每个字符串从下标 2 截取到下标 4,因为下标是从 0 开始的,因此也就是截取每个字符串中第 3 和第 4 个字符,可以看到每个输出的结果。3. strip() 函数该函数会去掉字符串中头和尾的空格已经换行符。# --- 解析 data 数据结果 --- 编程语言 推出时间 价格 主要创始人0 java 1995年 45.6 James Gosling1 python@163.com 1991年 67.0 Guido van Rossum\n2 C 1972年 33.9 Dennis MacAlistair Ritchie3 js@qq.com 1995年 59.5 Brendan Eich4 php 2012年\n 69.9 Rasmus Lerdorf5 C++ 1983年 75.0 Bjarne Stroustru# data 解析出来的数据# strip() 函数new_series=data["编程语言"]slice_res= new_series.str.strip()print(slice_res)# --- 输出结果 ---0 java1 python@163.com2 C3 js@qq.com4 php5 C++Name: 编程语言, dtype: object结果解析:通过输出结果可以看到,“C++” 中原数据后面有空格在,使用函数操作后,空格被删除。4. lower() 函数该函数用于将字符串中的所有大写字母转换为小写字母。# --- 解析 data 数据结果 --- 编程语言 推出时间 价格 主要创始人0 java 1995年 45.6 James Gosling1 python@163.com 1991年 67.0 Guido van Rossum\n2 C 1972年 33.9 Dennis MacAlistair Ritchie3 js@qq.com 1995年 59.5 Brendan Eich4 php 2012年\n 69.9 Rasmus Lerdorf5 C++ 1983年 75.0 Bjarne Stroustru# data 解析出来的数据# lower() 函数new_series=data["编程语言"]slice_res= new_series.str.lower() print(slice_res)# --- 输出结果 ---0 java1 python@163.com2 c3 js@qq.com4 php5 c++ Name: 编程语言, dtype: object结果解析:通过输出结果可以看到,“编程语言” 列所有的大写字母都转换成了小写。5. upper() 函数该函数用于将字符串中的所有小写字母转换为大写字母。# --- 解析 data 数据结果 --- 编程语言 推出时间 价格 主要创始人0 java 1995年 45.6 James Gosling1 python@163.com 1991年 67.0 Guido van Rossum\n2 C 1972年 33.9 Dennis MacAlistair Ritchie3 js@qq.com 1995年 59.5 Brendan Eich4 php 2012年\n 69.9 Rasmus Lerdorf5 C++ 1983年 75.0 Bjarne Stroustru# data 解析出来的数据# upper() 函数new_series=data["编程语言"]slice_res= new_series.str.upper() print(slice_res)# --- 输出结果 ---0 JAVA1 PYTHON@163.COM2 C3 JS@QQ.COM4 PHP5 C++ Name: 编程语言, dtype: object结果解析:通过输出结果可以看到,"编程语言" 这一列所有的小写字母均转换为了大写字母。6. cat() 函数该函数用于将两列合并成一列数据,前提是两列都是字符,如果是数值的话会报错。# --- 解析 data 数据结果 --- 编程语言 推出时间 价格 主要创始人0 java 1995年 45.6 James Gosling1 python@163.com 1991年 67.0 Guido van Rossum\n2 C 1972年 33.9 Dennis MacAlistair Ritchie3 js@qq.com 1995年 59.5 Brendan Eich4 php 2012年\n 69.9 Rasmus Lerdorf5 C++ 1983年 75.0 Bjarne Stroustrup# data 解析出来的数据# cat() 函数new_series=data["编程语言"]new_series_two=data["主要创始人"]slice_res= new_series.str.cat(new_series_two) print(slice_res)# --- 输出结果 ---0 javaJames Gosling1 python@163.comGuido van Rossum\n2 CDennis MacAlistair Ritchie3 js@qq.comBrendan Eich4 phpRasmus Lerdorf5 C++ Bjarne StroustrupName: 编程语言, dtype: object结果解析:通过 cat () 函数,将 “编程语言” 和 “主要创始人” 两列进行合并,可以看到输出结果中的合并结果。7. replace() 函数该函数可用于替换字符串的内容。# --- 解析 data 数据结果 --- 编程语言 推出时间 价格 主要创始人0 java 1995年 45.6 James Gosling1 python@163.com 1991年 67.0 Guido van Rossum\n2 C 1972年 33.9 Dennis MacAlistair Ritchie3 js@qq.com 1995年 59.5 Brendan Eich4 php 2012年\n 69.9 Rasmus Lerdorf5 C++ 1983年 75.0 Bjarne Stroustrup# data 解析出来的数据# replace() 函数new_series=data["编程语言"]slice_res= new_series.str.replace("com","cn") print(slice_res)# --- 输出结果 ---0 java1 python@163.cn2 C3 js@qq.cn4 php5 C++ Name: 编程语言, dtype: object 结果解析:这里我们将 “编程语言” 列字符串中的 com 替换为 cn,通过输出结果可以看到替换的效果。8. swapcase() 函数该函数用于将字符串中的大小写进行相互转换(大写转换为小写,小写转换为大写)。# --- 解析 data 数据结果 --- 编程语言 推出时间 价格 主要创始人0 java 1995年 45.6 James Gosling1 python@163.com 1991年 67.0 Guido van Rossum\n2 C 1972年 33.9 Dennis MacAlistair Ritchie3 js@qq.com 1995年 59.5 Brendan Eich4 php 2012年\n 69.9 Rasmus Lerdorf5 C++ 1983年 75.0 Bjarne Stroustrup# data 解析出来的数据# swapcase() 函数new_series=data["主要创始人"]slice_res= new_series.str.swapcase() print(slice_res)# --- 输出结果 ---0 jAMES gOSLING1 gUIDO VAN rOSSUM\n2 dENNIS mACaLISTAIR rITCHIE3 bRENDAN eICH4 rASMUS lERDORF5 bJARNE sTROUSTRUPName: 主要创始人, dtype: object 结果解析:这里我们通过 swapcase 函数将 “主要创始人” 列中字符串的大小写进行了互换,通过输出结果可以看到互换的效果。
- 2.3 shell 命令的执行过程 我们在上一节中介绍了 scrapy shell [url] 这样的指令,它帮助我们进入交互模式去执行调试获取网页的 xpath 表达式。我们有没有想过这个命令背后的原理?今天我们专门学习了 Scrapy Command,那么就正好借此机会看看 scrapy shell [url] 这条命令背后的原理是什么。根据前面跟踪代码的经验,我们可以直接定位到 scrapy/commands/shell.py 下 Command 类中的 run() 方法即可:# 源码位置: scrapy/commands/shell.py# ...class Command(ScrapyCommand): # ... def run(self, args, opts): url = args[0] if args else None if url: # first argument may be a local file url = guess_scheme(url) spider_loader = self.crawler_process.spider_loader spidercls = DefaultSpider if opts.spider: spidercls = spider_loader.load(opts.spider) elif url: # 如果传入了url参数,后面需要做请求 spidercls = spidercls_for_request(spider_loader, Request(url), spidercls, log_multiple=True) crawler = self.crawler_process._create_crawler(spidercls) crawler.engine = crawler._create_engine() crawler.engine.start() # 启动爬虫线程爬取url self._start_crawler_thread() shell = Shell(crawler, update_vars=self.update_vars, code=opts.code) shell.start(url=url, redirect=not opts.no_redirect) def _start_crawler_thread(self): t = Thread(target=self.crawler_process.start, kwargs={'stop_after_crawl': False}) t.daemon = True t.start()其实上面代码的执行逻辑是比较简单的,总的来说就做了2件事情:创建 scrapy 引擎并单独启动一个线程,后台运行;启动 shell 线程;我们来关注下 Shell 这个类:# 源码位置:scrapy/shell.py# ...class Shell: # ... def start(self, url=None, request=None, response=None, spider=None, redirect=True): # disable accidental Ctrl-C key press from shutting down the engine signal.signal(signal.SIGINT, signal.SIG_IGN) if url: self.fetch(url, spider, redirect=redirect) elif request: self.fetch(request, spider) elif response: request = response.request self.populate_vars(response, request, spider) else: self.populate_vars() if self.code: print(eval(self.code, globals(), self.vars)) else: """ Detect interactive shell setting in scrapy.cfg e.g.: ~/.config/scrapy.cfg or ~/.scrapy.cfg [settings] # shell can be one of ipython, bpython or python; # to be used as the interactive python console, if available. # (default is ipython, fallbacks in the order listed above) shell = python """ cfg = get_config() section, option = 'settings', 'shell' env = os.environ.get('SCRAPY_PYTHON_SHELL') shells = [] if env: shells += env.strip().lower().split(',') elif cfg.has_option(section, option): shells += [cfg.get(section, option).strip().lower()] else: # try all by default shells += DEFAULT_PYTHON_SHELLS.keys() # always add standard shell as fallback shells += ['python'] start_python_console(self.vars, shells=shells, banner=self.vars.pop('banner', ''))从上面的代码我们可以看到一点,当传入的参数有 url 或者 request 时,会调用 fetch() 方法去下载网页数据,它会调用 twisted 框架中的 threads 来执行网页的下载动作,并设置变量 response 。这就是为什么我们能在 scrapy shell 中直接使用 response 获取下载网页内容的原因。# 源码位置:scrapy/shell.py# ...class Shell: # ... def fetch(self, request_or_url, spider=None, redirect=True, **kwargs): from twisted.internet import reactor if isinstance(request_or_url, Request): request = request_or_url else: url = any_to_uri(request_or_url) request = Request(url, dont_filter=True, **kwargs) if redirect: request.meta['handle_httpstatus_list'] = SequenceExclude(range(300, 400)) else: request.meta['handle_httpstatus_all'] = True response = None try: response, spider = threads.blockingCallFromThread( reactor, self._schedule, request, spider) except IgnoreRequest: pass # 设置response结果 self.populate_vars(response, request, spider) def populate_vars(self, response=None, request=None, spider=None): import scrapy self.vars['scrapy'] = scrapy self.vars['crawler'] = self.crawler self.vars['item'] = self.item_class() self.vars['settings'] = self.crawler.settings self.vars['spider'] = spider self.vars['request'] = request self.vars['response'] = response if self.inthread: self.vars['fetch'] = self.fetch self.vars['view'] = open_in_browser self.vars['shelp'] = self.print_help self.update_vars(self.vars) if not self.code: self.vars['banner'] = self.get_help() # ...继续跟踪前面的 start() 方法,很明显我们的核心函数就是一句:start_python_console(self.vars, shells=shells, banner=self.vars.pop('banner', ''))self.vars 就是需要带到 shell 环境中的变量,shells 是我们选择交互的环境,后面可以看到总共支持4种交互环境,分别是 ptpython、ipython、ipython、和 python。banner 参数则表示进入交互模式是给出的提示语句。我们来看 start_python_console() 方法的源码:# 源码位置:scrapy/utils/console.py# ...DEFAULT_PYTHON_SHELLS = OrderedDict([ ('ptpython', _embed_ptpython_shell), ('ipython', _embed_ipython_shell), ('bpython', _embed_bpython_shell), ('python', _embed_standard_shell),])def get_shell_embed_func(shells=None, known_shells=None): """Return the first acceptable shell-embed function from a given list of shell names. """ if shells is None: # list, preference order of shells shells = DEFAULT_PYTHON_SHELLS.keys() if known_shells is None: # available embeddable shells known_shells = DEFAULT_PYTHON_SHELLS.copy() for shell in shells: if shell in known_shells: try: # function test: run all setup code (imports), # but dont fall into the shell return known_shells[shell]() except ImportError: continuedef start_python_console(namespace=None, banner='', shells=None): """Start Python console bound to the given namespace. Readline support and tab completion will be used on Unix, if available. """ if namespace is None: namespace = {} try: shell = get_shell_embed_func(shells) if shell is not None: shell(namespace=namespace, banner=banner) except SystemExit: # raised when using exit() in python code.interact pass通过分析代码可知:get_shell_embed_func() 方法最终会返回 DEFAULT_PYTHON_SHELLS 中对应值得那个,比如我们传入的 shells 值为 ['python'],则最后返回 _embed_standard_shell() 这个函数。最后就是调用这个函数,即可得到 scrapy shell 的交互模式。来最后看一看 _embed_standard_shell() 这个神奇的方法:# 源码位置:scrapy/utils/console.py# ...def _embed_standard_shell(namespace={}, banner=''): """Start a standard python shell""" import code try: # readline module is only available on unix systems import readline except ImportError: pass else: import rlcompleter # noqa: F401 readline.parse_and_bind("tab:complete") @wraps(_embed_standard_shell) def wrapper(namespace=namespace, banner=''): code.interact(banner=banner, local=namespace) return wrapper这段代码虽然简短,但它却是实现 scrapy shell 交互模式的核心方法。接下来,我们将基于上面这些方法来模拟构造一个简化的交互式模式来帮助我们更好的理解这些方法的作用。来看我抽取这些方法,简单编写的一个 test.py 脚本:[root@server2 shen]# cat test.py from functools import wrapsdef _embed_standard_shell(namespace={}, banner=''): """Start a standard python shell""" import code try: # readline module is only available on unix systems import readline except ImportError: pass else: import rlcompleter # noqa: F401 readline.parse_and_bind("tab:complete") @wraps(_embed_standard_shell) def wrapper(namespace=namespace, banner=''): code.interact(banner=banner, local=namespace) return wrapperdef start_python_console(namespace=None, banner='', shells=None): """Start Python console bound to the given namespace. Readline support and tab completion will be used on Unix, if available. """ if namespace is None: namespace = {} try: shell = _embed_standard_shell() shell(namespace=namespace, banner=banner) except SystemExit: # raised when using exit() in python code.interact passstart_python_console({'hello': 'world'}, banner='nothing to say')我们来运行下这个测试脚本看看效果:[root@server2 shen]# python3 test.py nothing to say>>> hello'world'>>> xxxTraceback (most recent call last): File "<console>", line 1, in <module>NameError: name 'xxx' is not defined>>> exit()是不是和 scrapy shell 交互式一模一样?到此为止,我们对 scrapy shell 这个命令已经分析的非常清楚了,大家是不是已经都理解了呢?
strip相关搜索
-
s line
safari浏览器
samba
SAMP
samplerate
sandbox
sanitize
saper
sas
sass
save
smarty模板
smil
smtp
snapshot
snd
snmptrap
soap
soapclient
soap协议