ホームページ >バックエンド開発 >Python チュートリアル >Pythonを使用してWebサイトのデータを保存する方法

Pythonを使用してWebサイトのデータを保存する方法

WBOY
WBOYオリジナル
2016-06-16 08:46:211092ブラウズ

エンコードの問題
中国語が関係するため、必然的にエンコードの問題が発生します。今回はこの機会に完全に理解しました。
問題はテキストのエンコードから始まります。元の英語のエンコードは 0 ~ 255 のみで、正確に 8 ビットと 1 バイトです。さまざまな言語を表現するには、当然拡張する必要があります。中国語ではGBシリーズがあります。 Unicode と UTF-8 について聞いたことがあるかもしれませんが、それらの間にはどのような関係があるのでしょうか?
Unicode は、ユニバーサル コードとしても知られるエンコード スキームであり、その範囲の広さを示しています。しかし、実際にコンピュータに保存されるときには、このエンコードは使用されず、仲介者の役割を果たしていると言えます。 Unicode を UTF-8 または GB にエンコードして、コンピューターに保存できます。 UTF-8 または GB をデコードして Unicode に復元することもできます。
Python では、Unicode は u' Chinese' など、u で始まるオブジェクトのタイプであり、string は別のタイプのオブジェクトで、特定のエンコード方式でコンピューター上に実際に存在する文字列です。たとえば、utf-8 エンコードの「中国語」と gbk エンコードの「中国語」は同じではありません。次のコードが表示されます:

コードをコピーします コードは次のとおりです:

>>> str=u'中文'
>> ;> str1=str.encode('utf8')
>>> str2=str.encode('gbk')
>>> 🎜>u 'u4e2du6587'
>'xe4xb8xadxe6x96x87'
'u4e2du6587'
'xd6xd0xcexc4'


ご覧のとおり、実際にコンピュータに保存されるのはこのエンコードだけであり、個々の漢字ではありません。印刷する場合、正しく印刷するにはどのエンコード方法が使用されたかを知る必要があります。 Python の Unicode は実際の文字列であり、文字列はバイト文字列であるとよく言われます。 ファイルのエンコーディング

エンコーディングが異なるため、コード ファイルに文字列を直接記述する場合、どのエンコーディングになりますか?これはファイルのエンコーディングによって決まります。ファイルは常に特定のエンコーディングで保存されます。 Python ファイルには、ファイルの保存に使用されるエンコード方法を示すコーディング ステートメントを記述することができます。宣言したエンコード方式と実際に保存したエンコード方式が一致しない場合、例外が発生します。次の例がわかります: utf-8 で保存されたファイルは gbk
として宣言されます

コードをコピー コードは次のとおりです:#coding:gbk
str=u'汉'
str1= str.encode('utf8')
str2=str.encode('gbk')
str3='中国語'
print repr(str)
print repr(str1)
印刷再現 (str2)
印刷再現 (str3)


プロンプト エラー ファイル "test.py"、行 1 SyntaxError: ファイル test.py の行 1 に非 ASCII 文字 'xe6' がありますが、エンコーディングが宣言されていません。 http://www.python.org/peps を参照してください。詳細については /pep-0263.html を に変更しました

コードをコピー コードは次のとおりです:#coding:utf8
str=u'汉'
str1= str.encode('utf8')
str2=str.encode('gbk')
str3='中国語'
print repr(str)
print repr(str1)
印刷再現 (str2)
印刷再現 (str3)


通常の結果を出力します u'u6c49' 'xe6xb1x89' 'xbaxba' 'xe6xb1x89'

基本的な方法

実際、Python で Web ページをクロールするのは非常に簡単で、簡単な文をいくつか書くだけです

コードをコピー コードは次のとおりです:import urllib2
page=urllib2.urlopen('url') .read( )


これにより、ページのコンテンツを取得できます。次に、通常のマッチングを使用して必要なコンテンツを一致させます。 しかし、実際にやるとなると色々細かい事が出てきます。

ログイン
ログイン認証が必要なWebサイトです。それほど難しいことではなく、cookielib ライブラリと urllib ライブラリをインポートするだけです。

コードをコピーします コードは次のとおりです。import urllib,urllib2,cookielib
cookiejar = cookielib.CookieJar( )
urlOpener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookiejar))


このようにして Cookie が読み込まれ、urlOpener を使用してログインした後も情報を記憶することができます。 切断と再接続

オープンをパッケージ化せずに上記のレベルに達するだけだと、ネットワークの状態が変動する限り、直接例外がスローされ、プログラム全体が終了します。これは非常に悪いプログラムです。現時点では、例外を処理して、さらに数回試してください:

コードをコピー コードは次のとおりです:

def multi_open(opener,*arg):
while True:
retryTimes=20
while retryTimes>0:
try:
Return opener.open(*arg) )
例外:
print '.',
retryTimes-=1

通常のマッチング
実際、通常のマッチングは耐障害性が非常に低く、Web ページを完全に統合する必要があるため、特に優れた方法ではありません。少しでも矛盾があると失敗します。後で、xpath に基づいて選択する方法があることがわかりました。次回はそれを試してみましょう。
実際には、正規表現を記述するための特定のテクニックがあります。
非貪欲マッチング。たとえば、hello のようなタグの場合、a を抽出したい場合、次のような式として記述しても機能しません。スパンクラス=.*>こんにちは。 * は欲張りマッチングを実行するためです。これは .?: hello を使用することです。
行間で一致します。行間の交差を実現する 1 つの方法は、改行が一致するように DOTALL フラグを使用することです。ただし、この場合、マッチングプロセス全体が非常に遅くなります。元のマッチングは行単位に基づいています。プロセス全体は最大でも O(nc2) で、n は行数、c は平均列数です。今は O((nc)2) になる可能性が非常に高いです。私の実装計画は、改行の一致に n を使用して、一致が最大何行にわたるかを明確にできるようにすることです。例: abcs*ns*def は、検索が 1 行おきに行われることを意味します。 (.n)? できるだけ少ない行に一致するように指定できます。
実はここでもう一つ注意すべき点があります。行末に r が付いている行もあります。つまり、行は rn で終わります。最初はこれがわからなかったので、長い間デバッグしました。ここで、s を直接使用してスペースを表し、行の末尾に r を使用します。
キャプチャのグループ化はありません。キャプチャされたグループ化に影響を与えないようにするには、上記の (.n) を (?:.n) に変更して、グループ化がキャプチャされたときに無視されるようにします。
単一の括弧はエスケープする必要があります。単一括弧は正規表現でグループ化を表現するために使用されるため、単一括弧と一致するようにエスケープする必要があります。使用するのに最適な通常の文字列は、r という接頭辞が付いた文字列です。そうでない場合は、エスケープする必要があります。
素早い正則化。たくさんパターンを書いたので、ルールもまとめてみました。まず、照合対象の文字に関連する段落を取り出します。一致するものは (.?) に置き換えられます。改行 n を文字列 sns* に置き換え、行の先頭と末尾のスペースを削除します。プロセス全体は vim で素早く書くことができます。
Excel操作
今回はExcelにデータを入れます。データベースに入れておけば、それほど苦労しないかもしれないと気づいたのは後になってからです。でも書き途中なので見返すのが大変です。
Excel を検索すると、いくつかの解決策が考えられます。その 1 つは、xlrt/xlwt ライブラリを使用することです。これは、Excel がコンピュータにインストールされているかどうかに関係なく実行できますが、xls 形式でのみ実行できます。もう 1 つは com を直接パッケージ化する方法で、これにはソフトウェアをコンピュータにインストールする必要があります。私は前者を使っています。
基本的な読み書きは問題ありません。しかし、データ量が増加すると、問題が発生します。
メモリが不足しています。プログラムの実行が開始されるとすぐに、メモリ使用量が少しずつ増加します。後でもう一度確認したところ、flush_row_data を使用する必要があることがわかりました。しかし、事態は依然としてうまくいきません。メモリ使用量を見ても特に問題はなく、非常に安定しています。しかし、最終的にはやはりメモリエラーが発生します。なんてこった。何度も確認して実行してください。全く結果が出ませんでした。恐ろしいことに、バグはデータ量が増加した場合にのみ発生し、データ量が増加するまでに数時間かかることがよくあり、デバッグのコストが非常に高くなります。偶然、メモリ使用量は一般的に安定しているものの、規則性のある小さなスパイクが発生すること、そしてこの規則性がフラッシュ_ロウ_データに関連しているかどうかを突然発見しました。私が疑問に思っていたのは、データがどこにフラッシュされるかということです。 xlwt のアプローチは非常に苦痛であることがわかりました。データをメモリに保存するか、一時ファイルにフラッシュして、保存時にすべてを一度に書き込みます。問題はこの 1 回の書き込みにあり、メモリが急増しています。では、flush_row_data は何に役立つのでしょうか?最初から書きたい場所に流し込んでみてはいかがでしょうか。
行数制限。これは xls 形式自体によって決定され、最大行数は 65536 のみです。しかもデータが大きいのでファイルを開くのも不便です。
上記の2点を踏まえ、最終的に行数が1000の倍数の場合はフラッシュする 行数が3を超える場合は新規シートを開く という方針になりました。シートの場合は、新しいファイルを作成します。便宜上、xlwt がパッケージ化されています

コードをコピー コードは次のとおりです:

#coding:utf-8#
import xlwt

class XLS:
'''xlwt をラップするクラス'''
MAX_ROW=65536
MAX_SHEET_NUM=3

def __init__(self,name,captionList,typeList,encoding='utf8',flushBound=1000):
self.name=name
self.captionList=captionList[:]
self.typeList=typeList[:]
self.workbookIndex=1
self.encoding=encoding
self.wb=xlwt.Workbook(encoding=self.encoding)
self.sheetIndex =1
self.__addSheet()
self.flushBound=flushBound

def __addSheet(self):
if self.sheetIndex != 1:
self.w b.save( self.name+str(self.workbookIndex)+'.xls')
if self.sheetIndex>XLS.MAX_SHEET_NUM:
self.workbookIndex+=1
self.wb=xlwt.Workbook(encoding=self) .encoding)
self.sheetIndex=1

self.sheet=self.wb.add_sheet(self.name.encode(self.encoding)+str(self.sheetIndex))
私にとってrange(len(self.captionList)):
self.sheet.write(0,i,self.captionList[i])

self.row=1

def write (self,data):
if self.row>=XLS.MAX_ROW:
self.sheetIndex += 1
self.__addSheet()

for i in range(len(data )):
if self.typeList[i]=="num":
try:
self.sheet.write(self.row,i,float(data[i]))
ただし、ValueError:
pass
else:
self.sheet.write(self.row,i,data[i])

if self.row % self.flushBoと == 0:
self.sheet.flush_row_data()
self.row+=1

def save(self):
self.wb.save(self.name+str(self.workbookIndex)+ '.xls')

変換ネットワーク特殊文字
ネットワークにも独自の変換文字があるため、適切なマッチングが行われる際にはいくつかの麻烦が存在します。安全を保つために、いくつかの修正が加えられています。

复制代码代码如下:

html_escape_table = {
"&": "&",
'"': """,
"'": "'",
">": ">",
"<": "<",
u"・":"・",
u"°":"°",
#正規表現
".":r".",
"^":r"^",
"$":r"$",
"{":r"{",
" }":r"}",
"\":r"\",
"|":r"|",
"(":r"(",
")" :r")",
"+":r"+",
"*":r"*",
"?":r"?",
}

def html_escape(text):
"""テキスト内のエンティティを生成します。"""
tmp="".join(html_escape_table.get(c,c) for c in text)
return tmp .encode("utf-8")

結論
以上です。しかし、最終的に書いたプログラムを読むのは耐えられませんでした。非常に悪いスタイル。まずは書いてみようと思いました。それから試してみると、変更したくなくなります。
最終的なプログラムの実行には長い時間がかかり、ネットワーク通信時間がそのほとんどを占めます。マルチスレッドを使用してリファクタリングすることを検討できますか?考えてみて、このままにしておきます。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。