>>211
String#scan は、何度もマッチを繰り返す場合に使うモノだと思うので、

body.each{ |line|
if /ID:(.*?)<dd>/ =~ line
id[$1] += 1
end
}

とするか、 String#scan を使うなら、

open(ARGV[0]).read.scan(/ID:(.*?)<dd>/) {
id[$1] += 1
}

ではないかと。
# IO#scan って無いのか・・・

あと、141 になるのは件の HTML を見てみれば当然というか、
件の HTML の総行数が 3141 行といえばわかる?