web-dev-qa-db-ja.com

Pythonで文字列からHTMLを取り除く

from mechanize import Browser
br = Browser()
br.open('http://somewebpage')
html = br.response().readlines()
for line in html:
  print line

HTMLファイルの行を印刷するとき、各HTML要素の内容のみを表示し、フォーマット自体を表示しない方法を探しています。 '<a href="whatever.com">some text</a>'が見つかった場合、「some text」のみを出力し、'<b>hello</b>'は「hello」などを出力します。これを行うにはどうすればよいでしょうか?

241
directedition

Python stdlibのみを必要とするため、私は常にこの関数を使用してHTMLタグを除去しました。

Python 2で

from HTMLParser import HTMLParser

class MLStripper(HTMLParser):
    def __init__(self):
        self.reset()
        self.fed = []
    def handle_data(self, d):
        self.fed.append(d)
    def get_data(self):
        return ''.join(self.fed)

def strip_tags(html):
    s = MLStripper()
    s.feed(html)
    return s.get_data()

Python 3の場合

from html.parser import HTMLParser

class MLStripper(HTMLParser):
    def __init__(self):
        self.reset()
        self.strict = False
        self.convert_charrefs= True
        self.fed = []
    def handle_data(self, d):
        self.fed.append(d)
    def get_data(self):
        return ''.join(self.fed)

def strip_tags(html):
    s = MLStripper()
    s.feed(html)
    return s.get_data()

:これは3.1のみで機能します。 3.2以降では、親クラスのinit関数を呼び出す必要があります。 Python 3.2でのHTMLParserの使用 を参照してください

386
Eloff

見落としがちなケースについてはあまり考えていませんが、簡単な正規表現を実行できます。

re.sub('<[^<]+?>', '', text)

正規表現を理解していない人のために、これは文字列<...>を検索します。ここで、内部コンテンツは+ではない1つ以上の(<)文字で構成されます。 ?は、見つけることができる最小の文字列と一致することを意味します。たとえば、<p>Hello</p>を指定すると、<'p>および</p>?と個別に一致します。これがないと、文字列<..Hello..>全体と一致します。

非タグの<がhtmlにある場合(例:2 < 3)、それはエスケープシーケンス&...として記述する必要があります。したがって、^<は不要です。

138
mmmdreg

なぜ皆が難しい方法でそれをするのですか? BeautifulSoup get_text()機能を使用できます。

from bs4 import BeautifulSoup

html_str = '''
<td><a href="http://www.fakewebsite.com">Please can you strip me?</a>
<br/><a href="http://www.fakewebsite.com">I am waiting....</a>
</td>
'''
soup = BeautifulSoup(html_str)

print(soup.get_text()) 
#or via attribute of Soup Object: print(soup.text)
41
Aminah Nuraini

短縮版!

import re, cgi
tag_re = re.compile(r'(<!--.*?-->|<[^>]*>)')

# Remove well-formed tags, fixing mistakes by legitimate users
no_tags = tag_re.sub('', user_input)

# Clean up anything else by escaping
ready_for_web = cgi.escape(no_tags)

正規表現ソース:MarkupSafe 。このバージョンはHTMLエンティティも処理しますが、このクイックエンティティは処理しません。

タグを削除してそのまま残せないのはなぜですか?

isを浮かせたままにせずに、人々を<i>italicizing</i>から守ることは一つのことです。しかし、任意の入力を取得して完全に無害にすることは別です。このページのテクニックのほとんどは、閉じられていないコメント(<!--)やタグの一部ではない山括弧(blah <<<><blah)などをそのまま残します。 HTMLParserバージョンは、閉じられていないコメント内にある場合、完全なタグを残すことさえできます。

テンプレートが{{ firstname }} {{ lastname }}の場合はどうなりますか? firstname = '<a'lastname = 'href="http://evil.com/">'は、このページのすべてのタグストリッパー(@Medeirosを除く!)によって許可されます。これらは、独自の完全なタグではないためです。通常のHTMLタグを取り除くだけでは十分ではありません。

Djangoのstrip_tagsは、この質問に対するトップアンサーの改良版(次の見出しを参照)であり、次の警告を提供します。

結果の文字列がHTMLセーフであるという保証はまったくありません。したがって、たとえばescape()を使用して、最初にエスケープせずにstrip_tags呼び出しの結果を安全にマークしないでください。

彼らのアドバイスに従ってください!

HTMLParserでタグを削除するには、複数回実行する必要があります。

この質問に対するトップアンサーを回避するのは簡単です。

この文字列を見てください( ソースとディスカッション ):

<img<!-- --> src=x onerror=alert(1);//><!-- -->

HTMLParserが最初に見たとき、<img...>がタグであることはわかりません。壊れているように見えるので、HTMLParserはそれを取り除きません。 <!-- comments -->のみを取り出して、

<img src=x onerror=alert(1);//>

この問題は、2014年3月にDjangoプロジェクトに開示されました。彼らの古いstrip_tagsは、本質的にこの質問に対する一番上の回答と同じでした。 新しいバージョン 基本的には、再度実行しても文字列が変更されなくなるまでループで実行されます。

# _strip_once runs HTMLParser once, pulling out just the text of all the nodes.

def strip_tags(value):
    """Returns the given HTML with all tags stripped."""
    # Note: in typical case this loop executes _strip_once once. Loop condition
    # is redundant, but helps to reduce number of executions of _strip_once.
    while '<' in value and '>' in value:
        new_value = _strip_once(value)
        if len(new_value) >= len(value):
            # _strip_once was not able to detect more tags
            break
        value = new_value
    return value

もちろん、常にstrip_tags()の結果をエスケープする場合、これは問題になりません。

2015年3月19日更新:1.4.20、1.6.11、1.7.7、および1.8c1より前のバージョンのDjangoにはバグがありました。これらのバージョンは、strip_tags()関数で無限ループに入る可能性があります。修正バージョンは上記に再現されています。 詳細はこちら

コピーまたは使用する良いもの

私のサンプルコードはHTMLエンティティを処理しません-DjangoおよびMarkupSafeパッケージバージョンは処理します。

私のサンプルコードは、クロスサイトスクリプティング防止のための優れた MarkupSafe ライブラリから取得されています。便利で高速です(ネイティブPythonバージョンへのCの高速化により)。 Google App Engine に含まれており、 Jinja2(2.7以降) 、Mako、Pylonsなどで使用されています。 Django 1.7のDjangoテンプレートで簡単に動作します。

Djangoのstrip_tagsと他のhtmlユーティリティの最新バージョンは優れていますが、MarkupSafeほど便利ではありません。これらはかなり自己完結型で、必要なものを このファイル からコピーできます。

すべてのタグをalmost除去する必要がある場合は、 Bleach ライブラリが適しています。 「ユーザーはイタリック体にすることはできますが、iframeを作成することはできません」などのルールを適用できます。

タグストリッパーのプロパティを理解してください!ファズテストを実行してください! ここにコードがあります 私はこの答えの研究をしていました。

sheepish note-質問自体はコンソールへの印刷に関するものですが、これは「python strip html from string」に対するGoogleの最高の結果であるため、この答えは約99%です。ウェブ。

29
rescdsk

タグを削除する方法HTMLエンティティをプレーンテキストにデコードする方法が必要でした。次のソリューションは、Eloffの回答に基づいています(エンティティを削除するため使用できませんでした)。

from HTMLParser import HTMLParser
import htmlentitydefs

class HTMLTextExtractor(HTMLParser):
    def __init__(self):
        HTMLParser.__init__(self)
        self.result = [ ]

    def handle_data(self, d):
        self.result.append(d)

    def handle_charref(self, number):
        codepoint = int(number[1:], 16) if number[0] in (u'x', u'X') else int(number)
        self.result.append(unichr(codepoint))

    def handle_entityref(self, name):
        codepoint = htmlentitydefs.name2codepoint[name]
        self.result.append(unichr(codepoint))

    def get_text(self):
        return u''.join(self.result)

def html_to_text(html):
    s = HTMLTextExtractor()
    s.feed(html)
    return s.get_text()

簡単なテスト:

html = u'<a href="#">Demo <em>(&not; \u0394&#x03b7;&#956;&#x03CE;)</em></a>'
print repr(html_to_text(html))

結果:

u'Demo (\xac \u0394\u03b7\u03bc\u03ce)'

エラー処理:

  • 無効なHTML構造は、 HTMLParseError を引き起こす可能性があります。
  • 無効な名前付きHTMLエンティティ(&#apos;など、XMLおよびXHTMLで有効ですが、プレーンHTMLではない)は、ValueError例外を発生させます。
  • Pythonで許容されるUnicode範囲外のコードポイント(一部のシステムでは Basic Multilingual Plane 以外の文字など)を指定する数値HTMLエンティティは、ValueError例外を発生させます。

セキュリティ上の注意:HTML除去(HTMLをプレーンテキストに変換)とHTMLサニタイズ(プレーンテキストをHTMLに変換)を混同しないでください。この答えは、HTMLを削除し、エンティティをプレーンテキストにデコードします。これにより、結果をHTMLコンテキストで安全に使用できなくなります。

例:&lt;script&gt;alert("Hello");&lt;/script&gt;<script>alert("Hello");</script>に変換されます。これは100%正しい動作ですが、結果のプレーンテキストがそのままHTMLページに挿入される場合は明らかに不十分です。

ルールは難しくありません:いつでもプレーンテキスト文字列をHTML出力に挿入する場合、alwaysHTML escape( cgi.escape(s, True)を使用して、HTMLが含まれていないことを「知っている」場合でも(たとえば、HTMLコンテンツを削除したため)。

(ただし、OPは結果をコンソールに出力するように要求しました。この場合、HTMLエスケープは必要ありません。)

Python 3.4以降のバージョン:(with doctest!)

import html.parser

class HTMLTextExtractor(html.parser.HTMLParser):
    def __init__(self):
        super(HTMLTextExtractor, self).__init__()
        self.result = [ ]

    def handle_data(self, d):
        self.result.append(d)

    def get_text(self):
        return ''.join(self.result)

def html_to_text(html):
    """Converts HTML to plain text (stripping tags and converting entities).
    >>> html_to_text('<a href="#">Demo<!--...--> <em>(&not; \u0394&#x03b7;&#956;&#x03CE;)</em></a>')
    'Demo (\xac \u0394\u03b7\u03bc\u03ce)'

    "Plain text" doesn't mean result can safely be used as-is in HTML.
    >>> html_to_text('&lt;script&gt;alert("Hello");&lt;/script&gt;')
    '<script>alert("Hello");</script>'

    Always use html.escape to sanitize text before using in an HTML context!

    HTMLParser will do its best to make sense of invalid HTML.
    >>> html_to_text('x < y &lt z <!--b')
    'x < y < z '

    Unrecognized named entities are included as-is. '&apos;' is recognized,
    despite being XML only.
    >>> html_to_text('&nosuchentity; &apos; ')
    "&nosuchentity; ' "
    """
    s = HTMLTextExtractor()
    s.feed(html)
    return s.get_text()

Python 3ではHTMLParserが改善されていることに注意してください(つまり、コードが少なくなり、エラー処理が改善されます)。

28
Søren Løvborg

これには簡単な方法があります:

def remove_html_markup(s):
    tag = False
    quote = False
    out = ""

    for c in s:
            if c == '<' and not quote:
                tag = True
            Elif c == '>' and not quote:
                tag = False
            Elif (c == '"' or c == "'") and tag:
                quote = not quote
            Elif not tag:
                out = out + c

    return out

ここでアイデアを説明します: http://youtu.be/2tu9LTDujbw

ここで動作していることがわかります: http://youtu.be/HPkNPcYed9M?t=35s

PS-クラスに興味がある場合(Pythonのスマートデバッグについて)リンクを提供します: http://www.udacity.com/overview/Course/cs259/CourseRev/1 。それは無料です!

どういたしまして! :)

18
Medeiros

HTMLエンティティ(つまり_&amp;)を保持する必要がある場合、「handle_entityref」メソッドを Eloffの答え に追加しました。

from HTMLParser import HTMLParser

class MLStripper(HTMLParser):
    def __init__(self):
        self.reset()
        self.fed = []
    def handle_data(self, d):
        self.fed.append(d)
    def handle_entityref(self, name):
        self.fed.append('&%s;' % name)
    def get_data(self):
        return ''.join(self.fed)

def html_to_text(html):
    s = MLStripper()
    s.feed(html)
    return s.get_data()
16
Robert

すべてのHTMLタグを削除したい場合、私が見つけた最も簡単な方法はBeautifulSoupを使用することです。

from bs4 import BeautifulSoup  # Or from BeautifulSoup import BeautifulSoup

def stripHtmlTags(htmlTxt):
    if htmlTxt is None:
            return None
        else:
            return ''.join(BeautifulSoup(htmlTxt).findAll(text=True)) 

受け入れられた答えのコードを試しましたが、「RuntimeError:maximum recursion depth exceeded」を取得していましたが、上記のコードブロックでは発生しませんでした。

12
Vasilis

lxml.html -basedソリューション(lxmlはネイティブライブラリであるため、純粋なPythonソリューションよりもはるかに高速です)。

from lxml import html
from lxml.html.clean import clean_html

tree = html.fromstring("""<span class="item-summary">
                            Detailed answers to any questions you might have
                        </span>""")

print(clean_html(tree).strip())

# >>> Detailed answers to any questions you might have

http://lxml.de/lxmlhtml.html#cleaning-up-html を参照して、lxml.cleanerの正確な動作を確認してください。

テキストに変換する前に何をサニタイズするかを厳密に制御する必要がある場合は、コンストラクタで 希望するオプション を渡すことで lxml Cleaner を明示的に使用できます。

cleaner = Cleaner(page_structure=True,
                  meta=True,
                  embedded=True,
                  links=True,
                  style=True,
                  processing_instructions=True,
                  inline_style=True,
                  scripts=True,
                  javascript=True,
                  comments=True,
                  frames=True,
                  forms=True,
                  annoying_tags=True,
                  remove_unknown_tags=True,
                  safe_attrs_only=True,
                  safe_attrs=frozenset(['src','color', 'href', 'title', 'class', 'name', 'id']),
                  remove_tags=('span', 'font', 'div')
                  )
sanitized_html = cleaner.clean_html(unsafe_html)
9
ccpizza

Beautiful Soupパッケージはこれをすぐに行います。

from bs4 import BeautifulSoup

soup = BeautifulSoup(html)
text = soup.get_text()
print(text)
7
runawaykid

別のHTMLパーサー( lxml 、または Beautiful Soup )を使用できます。これは、テキストのみを抽出する機能を提供するものです。または、タグを取り除くライン文字列で正規表現を実行できます。詳細については、 http://www.amk.ca/python/howto/regex/ を参照してください。

2
Jason Coon

あるプロジェクトでは、HTMLだけでなく、cssとjsも削除する必要がありました。したがって、私はEloffsの回答のバリエーションを作りました:

class MLStripper(HTMLParser):
    def __init__(self):
        self.reset()
        self.strict = False
        self.convert_charrefs= True
        self.fed = []
        self.css = False
    def handle_starttag(self, tag, attrs):
        if tag == "style" or tag=="script":
            self.css = True
    def handle_endtag(self, tag):
        if tag=="style" or tag=="script":
            self.css=False
    def handle_data(self, d):
        if not self.css:
            self.fed.append(d)
    def get_data(self):
        return ''.join(self.fed)

def strip_tags(html):
    s = MLStripper()
    s.feed(html)
    return s.get_data()
1
mousetail

Søren-løvborgの答えのPython 3適応

from html.parser import HTMLParser
from html.entities import html5

class HTMLTextExtractor(HTMLParser):
    """ Adaption of http://stackoverflow.com/a/7778368/196732 """
    def __init__(self):
        super().__init__()
        self.result = []

    def handle_data(self, d):
        self.result.append(d)

    def handle_charref(self, number):
        codepoint = int(number[1:], 16) if number[0] in (u'x', u'X') else int(number)
        self.result.append(unichr(codepoint))

    def handle_entityref(self, name):
        if name in html5:
            self.result.append(unichr(html5[name]))

    def get_text(self):
        return u''.join(self.result)

def html_to_text(html):
    s = HTMLTextExtractor()
    s.feed(html)
    return s.get_text()
1
CpILL

HTML-Parserを使用したソリューションは、実行が1回のみの場合、すべて壊れやすくなります。

html_to_text('<<b>script>alert("hacked")<</b>/script>

結果:

<script>alert("hacked")</script>

あなたが防止しようとするもの。 HTMLパーサーを使用する場合、ゼロが置換されるまでタグをカウントします。

from HTMLParser import HTMLParser

class MLStripper(HTMLParser):
    def __init__(self):
        self.reset()
        self.fed = []
        self.containstags = False

    def handle_starttag(self, tag, attrs):
       self.containstags = True

    def handle_data(self, d):
        self.fed.append(d)

    def has_tags(self):
        return self.containstags

    def get_data(self):
        return ''.join(self.fed)

def strip_tags(html):
    must_filtered = True
    while ( must_filtered ):
        s = MLStripper()
        s.feed(html)
        html = s.get_data()
        must_filtered = s.has_tags()
    return html
1
Falk Nisius

これは簡単な修正であり、さらに最適化できますが、問題なく動作します。このコードは、空ではないすべてのタグを「」で置き換え、指定された入力テキストからすべてのhtmlタグを取り除きます。/file.py input outputを使用して実行できます

    #!/usr/bin/python
import sys

def replace(strng,replaceText):
    rpl = 0
    while rpl > -1:
        rpl = strng.find(replaceText)
        if rpl != -1:
            strng = strng[0:rpl] + strng[rpl + len(replaceText):]
    return strng


lessThanPos = -1
count = 0
listOf = []

try:
    #write File
    writeto = open(sys.argv[2],'w')

    #read file and store it in list
    f = open(sys.argv[1],'r')
    for readLine in f.readlines():
        listOf.append(readLine)         
    f.close()

    #remove all tags  
    for line in listOf:
        count = 0;  
        lessThanPos = -1  
        lineTemp =  line

            for char in lineTemp:

            if char == "<":
                lessThanPos = count
            if char == ">":
                if lessThanPos > -1:
                    if line[lessThanPos:count + 1] != '<>':
                        lineTemp = replace(lineTemp,line[lessThanPos:count + 1])
                        lessThanPos = -1
            count = count + 1
        lineTemp = lineTemp.replace("&lt","<")
        lineTemp = lineTemp.replace("&gt",">")                  
        writeto.write(lineTemp)  
    writeto.close() 
    print "Write To --- >" , sys.argv[2]
except:
    print "Help: invalid arguments or exception"
    print "Usage : ",sys.argv[0]," inputfile outputfile"
1
kiran Mohan

Python 3.1でEloffの答えをうまく使いました[本当にありがとう!]。

Python 3.2.3にアップグレードすると、エラーが発生しました。

here 応答者Thomas Kのおかげで提供された解決策は、次のコードにsuper().__init__()を挿入することです。

def __init__(self):
    self.reset()
    self.fed = []

...次のように表示するには:

def __init__(self):
    super().__init__()
    self.reset()
    self.fed = []

...そして、Python 3.2.3で動作します。

繰り返しになりますが、上記の修正とEloffの元のコードを提供してくれたThomas Kに感謝します!

1
MilesNielsen

これは、現在受け入れられている回答( https://stackoverflow.com/a/925630/95989 )に似たソリューションです。ただし、内部HTMLParserクラスを直接使用する(つまり、サブクラス化しない)ことを除きます。はるかに簡潔:

 def strip_html(text):
 parts = [] 
 parser = HTMLParser()
 parser.handle_data = parts.append 
 parser.feed (テキスト)
 return '' .join(parts)
1
Richard

独自の関数を作成できます。

def StripTags(text):
     finished = 0
     while not finished:
         finished = 1
         start = text.find("<")
         if start >= 0:
             stop = text[start:].find(">")
             if stop >= 0:
                 text = text[:start] + text[start+stop+1:]
                 finished = 0
     return text
0
Yuda Prawira

これがpython 3のソリューションです。

import html
import re

def html_to_txt(html_text):
    ## unescape html
    txt = html.unescape(html_text)
    tags = re.findall("<[^>]+>",txt)
    print("found tags: ")
    print(tags)
    for tag in tags:
        txt=txt.replace(tag,'')
    return txt

それが完璧かどうかはわかりませんが、私のユースケースを解決し、シンプルに見えます。

0

シンプルなコード!これにより、その中のすべての種類のタグとコンテンツが削除されます。

def rm(s):
    start=False
    end=False
    s=' '+s
    for i in range(len(s)-1):
        if i<len(s):
            if start!=False:
                if s[i]=='>':
                    end=i
                    s=s[:start]+s[end+1:]
                    start=end=False
            else:
                if s[i]=='<':
                    start=i
    if s.count('<')>0:
        self.rm(s)
    else:
        s=s.replace('&nbsp;', ' ')
        return s

ただし、テキストに<>記号が含まれている場合、完全な結果は得られません。

0
Vanjith

私はGithubのreadmeを解析していますが、次のことが本当にうまくいくことがわかりました。

import re
import lxml.html

def strip_markdown(x):
    links_sub = re.sub(r'\[(.+)\]\([^\)]+\)', r'\1', x)
    bold_sub = re.sub(r'\*\*([^*]+)\*\*', r'\1', links_sub)
    emph_sub = re.sub(r'\*([^*]+)\*', r'\1', bold_sub)
    return emph_sub

def strip_html(x):
    return lxml.html.fromstring(x).text_content() if x else ''

その後

readme = """<img src="https://raw.githubusercontent.com/kootenpv/sky/master/resources/skylogo.png" />

            sky is a web scraping framework, implemented with the latest python versions in mind (3.4+). 
            It uses the asynchronous `asyncio` framework, as well as many popular modules 
            and extensions.

            Most importantly, it aims for **next generation** web crawling where machine intelligence 
            is used to speed up the development/maintainance/reliability of crawling.

            It mainly does this by considering the user to be interested in content 
            from *domains*, not just a collection of *single pages*
            ([templating approach](#templating-approach))."""

strip_markdown(strip_html(readme))

すべてのマークダウンとHTMLを正しく削除します。

0
PascalVKooten

hext は、とりわけHTML strip が可能なパッケージです。 beautifulsoupの代替です。以下はhext==0.2.3でテストされました。

これをユーティリティモジュールに保存します。 util/hext.py

import hext

_HTML_TEXT_RULE = hext.Rule('<html @text:text />')


def html_to_text(text: str) -> str:
    # Ref: https://stackoverflow.com/a/56894409/
    return _HTML_TEXT_RULE.extract(hext.Html(f'<html>{text}</html>'))[0]['text']

使用例:

>>> from .util.hext import html_to_text

>>> html_to_text('<b>Hello world!</b>')
'Hello world!'

>>> html_to_text('<a href="google.com">some text</a>')
'some text'

>>> html_to_text('<span class="small-caps">l</span>-arginine minimizes immunosuppression and prothrombin time and enhances the genotoxicity of 5-fluorouracil in rats')
'l-arginine minimizes immunosuppression and prothrombin time and enhances the genotoxicity of 5-fluorouracil in rats'

>>> html_to_text('Attenuation of diabetic nephropathy by dietary fenugreek (<em>Trigonella foenum-graecum</em>) seeds and onion (<em>Allium cepa</em>) <em>via</em> suppression of glucose transporters and renin-angiotensin system')
'Attenuation of diabetic nephropathy by dietary fenugreek (Trigonella foenum-graecum) seeds and onion (Allium cepa) via suppression of glucose transporters and renin-angiotensin system'

不正なHTMLの使用例:

>>> html_to_text('<b>Hello <i>world!')
'Hello world!'

>>> html_to_text('<a href="google.com">some <faketag>text')
'some text'
0
Acumenus

BeautifulSoup、html2text、または@Eloffのコードを使用すると、ほとんどの場合、いくつかのhtml要素、javascriptコードが残ります...

したがって、これらのライブラリの組み合わせを使用して、マークダウン形式を削除できます(Python 3):

import re
import html2text
from bs4 import BeautifulSoup
def html2Text(html):
    def removeMarkdown(text):
        for current in ["^[ #*]{2,30}", "^[ ]{0,30}\d\\\.", "^[ ]{0,30}\d\."]:
            markdown = re.compile(current, flags=re.MULTILINE)
            text = markdown.sub(" ", text)
        return text
    def removeAngular(text):
        angular = re.compile("[{][|].{2,40}[|][}]|[{][*].{2,40}[*][}]|[{][{].{2,40}[}][}]|\[\[.{2,40}\]\]")
        text = angular.sub(" ", text)
        return text
    h = html2text.HTML2Text()
    h.images_to_alt = True
    h.ignore_links = True
    h.ignore_emphasis = False
    h.skip_internal_links = True
    text = h.handle(html)
    soup = BeautifulSoup(text, "html.parser")
    text = soup.text
    text = removeAngular(text)
    text = removeMarkdown(text)
    return text

それは私にはうまくいきますが、もちろん強化することができます...

0
hayj